/*************************************************************************/ /* cp_loader_it_samples.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ /* "Software"), to deal in the Software without restriction, including */ /* without limitation the rights to use, copy, modify, merge, publish, */ /* distribute, sublicense, and/or sell copies of the Software, and to */ /* permit persons to whom the Software is furnished to do so, subject to */ /* the following conditions: */ /* */ /* The above copyright notice and this permission notice shall be */ /* included in all copies or substantial portions of the Software. */ /* */ /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "cp_loader_it.h" #include "cp_sample.h" struct AuxSampleData { uint32_t fileofs; uint32_t c5spd; uint32_t length; uint32_t loop_begin; uint32_t loop_end; bool loop_enabled; bool pingpong_enabled; bool is16bit; bool stereo; bool exists; bool compressed; }; enum IT_Sample_Flags { IT_SAMPLE_EXISTS = 1, IT_SAMPLE_16BITS = 2, IT_SAMPLE_STEREO = 4, IT_SAMPLE_COMPRESSED = 8, IT_SAMPLE_LOOPED = 16, IT_SAMPLE_SUSTAIN_LOOPED = 32, IT_SAMPLE_LOOP_IS_PINGPONG = 64, IT_SAMPLE_SUSTAIN_LOOP_IS_PINGPONG = 128 }; CPLoader::Error CPLoader_IT::load_sample(CPSample *p_sample) { AuxSampleData aux_sample_data; char aux_header[4]; file->get_byte_array((uint8_t *)aux_header, 4); if (aux_header[0] != 'I' || aux_header[1] != 'M' || aux_header[2] != 'P' || aux_header[3] != 'S') { //CP_PRINTERR("IT CPLoader CPSample: Failed Identifier"); return FILE_UNRECOGNIZED; } // Ignore deprecated 8.3 filename for (int i = 0; i < 12; i++) file->get_byte(); file->get_byte(); //ignore zerobyte p_sample->set_global_volume(file->get_byte()); /* SAMPLE FLAGS */ uint8_t flags = file->get_byte(); aux_sample_data.loop_enabled = flags & IT_SAMPLE_LOOPED; aux_sample_data.pingpong_enabled = flags & IT_SAMPLE_LOOP_IS_PINGPONG; aux_sample_data.is16bit = flags & IT_SAMPLE_16BITS; aux_sample_data.exists = flags & IT_SAMPLE_EXISTS; aux_sample_data.stereo = flags & IT_SAMPLE_STEREO; aux_sample_data.compressed = flags & IT_SAMPLE_COMPRESSED; p_sample->set_default_volume(file->get_byte()); /* SAMPLE NAME */ char aux_name[26]; file->get_byte_array((uint8_t *)aux_name, 26); p_sample->set_name(aux_name); // ?? uint8_t convert_flag = file->get_byte(); // PAN uint8_t pan = file->get_byte(); p_sample->set_pan(pan & 0x7F); p_sample->set_pan_enabled(pan & 0x80); aux_sample_data.length = file->get_dword(); aux_sample_data.loop_begin = file->get_dword(); aux_sample_data.loop_end = file->get_dword(); aux_sample_data.c5spd = file->get_dword(); /*p_sample->data.set_sustain_loop_begin=*/file->get_dword(); /*p_sample->data.sustain_loop_end=*/file->get_dword(); aux_sample_data.fileofs = file->get_dword(); p_sample->set_vibrato_speed(file->get_byte()); p_sample->set_vibrato_depth(file->get_byte()); p_sample->set_vibrato_rate(file->get_byte()); switch (file->get_byte()) { /* Vibrato Wave: 0=sine, 1=rampdown, 2=square, 3=random */ case 0: p_sample->set_vibrato_type(CPSample::VIBRATO_SINE); break; case 1: p_sample->set_vibrato_type(CPSample::VIBRATO_SAW); break; case 2: p_sample->set_vibrato_type(CPSample::VIBRATO_SQUARE); break; case 3: p_sample->set_vibrato_type(CPSample::VIBRATO_RANDOM); break; default: p_sample->set_vibrato_type(CPSample::VIBRATO_SINE); break; } //printf("Name %s - Flags: fileofs :%i - c5spd %i - len %i 16b %i - data?: %i\n",p_sample->get_name(),aux_sample_data.fileofs,aux_sample_data.c5spd, aux_sample_data.length, aux_sample_data.is16bit,aux_sample_data.exists); CPSample_ID samp_id; if (aux_sample_data.exists) { samp_id = load_sample_data(aux_sample_data); CPSampleManager::get_singleton()->set_c5_freq(samp_id, aux_sample_data.c5spd); CPSampleManager::get_singleton()->set_loop_begin(samp_id, aux_sample_data.loop_begin); CPSampleManager::get_singleton()->set_loop_end(samp_id, aux_sample_data.loop_end); CPSample_Loop_Type loop_type = aux_sample_data.loop_enabled ? (aux_sample_data.pingpong_enabled ? CP_LOOP_BIDI : CP_LOOP_FORWARD) : CP_LOOP_NONE; CPSampleManager::get_singleton()->set_loop_end(samp_id, aux_sample_data.loop_end); CPSampleManager::get_singleton()->set_loop_type(samp_id, loop_type); } //printf("Loaded id is null?: %i\n",samp_id.is_null()); p_sample->set_sample_data(samp_id); if (!samp_id.is_null()) { // printf("Loaded ID: stereo: %i len %i 16bit %i\n",CPSampleManager::get_singleton()->is_stereo(samp_id), CPSampleManager::get_singleton()->get_size( samp_id), CPSampleManager::get_singleton()->is_16bits( samp_id) ); } CP_ERR_COND_V(file->eof_reached(), FILE_CORRUPTED); CP_ERR_COND_V(file->get_error(), FILE_CORRUPTED); return FILE_OK; } CPSample_ID CPLoader_IT::load_sample_data(AuxSampleData &p_sample_data) { int aux_sample_properties = (p_sample_data.is16bit ? IT_SAMPLE_16BITS : 0) | (p_sample_data.compressed ? IT_SAMPLE_COMPRESSED : 0) | (p_sample_data.stereo ? IT_SAMPLE_STEREO : 0); file->seek(p_sample_data.fileofs); CPSampleManager *sm = CPSampleManager::get_singleton(); CPSample_ID id; switch (aux_sample_properties) { case (0): // 8 bits, mono case (IT_SAMPLE_16BITS): // 16 bits mono case (IT_SAMPLE_STEREO): // 8 bits stereo case (IT_SAMPLE_16BITS | IT_SAMPLE_STEREO): { // 16 bits mono id = sm->create(p_sample_data.is16bit, p_sample_data.stereo, p_sample_data.length); if (id.is_null()) break; sm->lock_data(id); int16_t *ptr16 = (int16_t *)sm->get_data(id); int8_t *ptr8 = (int8_t *)ptr16; int chans = p_sample_data.stereo ? 2 : 1; if (p_sample_data.is16bit) { for (int c = 0; c < chans; c++) { for (int i = 0; i < p_sample_data.length; i++) { ptr16[i * chans + c] = file->get_word(); } } } else { for (int c = 0; c < chans; c++) { for (int i = 0; i < p_sample_data.length; i++) { ptr8[i * chans + c] = file->get_byte(); } } } sm->unlock_data(id); } break; case (IT_SAMPLE_COMPRESSED): { // 8 bits compressed id = sm->create(false, false, p_sample_data.length); if (id.is_null()) break; sm->lock_data(id); if (load_sample_8bits_IT_compressed((void *)sm->get_data(id), p_sample_data.length)) { sm->unlock_data(id); sm->destroy(id); break; } sm->unlock_data(id); } break; case (IT_SAMPLE_16BITS | IT_SAMPLE_COMPRESSED): { // 16 bits compressed id = sm->create(true, false, p_sample_data.length); if (id.is_null()) break; sm->lock_data(id); if (load_sample_16bits_IT_compressed((void *)sm->get_data(id), p_sample_data.length)) { sm->unlock_data(id); sm->destroy(id); break; } sm->unlock_data(id); } break; default: { // I dont know how to handle stereo compressed, does that exist? } break; } return id; } CPLoader::Error CPLoader_IT::load_samples() { for (int i = 0; i < header.smpnum; i++) { //seek to sample file->seek(0xC0 + header.ordnum + header.insnum * 4 + i * 4); uint32_t final_location = file->get_dword(); file->seek(final_location); Error err = load_sample(song->get_sample(i)); CP_ERR_COND_V(err, err); } if (file->eof_reached() || file->get_error()) return FILE_CORRUPTED; return FILE_OK; } /* * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE -The following sample decompression code is based on xmp's code.(http://xmp.helllabs.org) which is based in openCP code. * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE * NOTICE */ uint32_t CPLoader_IT::read_n_bits_from_IT_compressed_block(uint8_t p_bits_to_read) { uint32_t aux_return_value; uint32_t val; uint8_t *buffer = (uint8_t *)source_position; if (p_bits_to_read <= source_remaining_bits) { val = buffer[3]; val <<= 8; val |= buffer[2]; val <<= 8; val |= buffer[1]; val <<= 8; val |= buffer[0]; aux_return_value = val & ((1 << p_bits_to_read) - 1); val >>= p_bits_to_read; source_remaining_bits -= p_bits_to_read; buffer[3] = val >> 24; buffer[2] = (val >> 16) & 0xFF; buffer[1] = (val >> 8) & 0xFF; buffer[0] = (val)&0xFF; } else { aux_return_value = buffer[3]; aux_return_value <<= 8; aux_return_value |= buffer[2]; aux_return_value <<= 8; aux_return_value |= buffer[1]; aux_return_value <<= 8; aux_return_value |= buffer[0]; uint32_t nbits = p_bits_to_read - source_remaining_bits; source_position++; buffer += 4; val = buffer[3]; val <<= 8; val |= buffer[2]; val <<= 8; val |= buffer[1]; val <<= 8; val |= buffer[0]; aux_return_value |= ((val & ((1 << nbits) - 1)) << source_remaining_bits); val >>= nbits; source_remaining_bits = 32 - nbits; buffer[3] = val >> 24; buffer[2] = (val >> 16) & 0xFF; buffer[1] = (val >> 8) & 0xFF; buffer[0] = (val)&0xFF; } return aux_return_value; } bool CPLoader_IT::read_IT_compressed_block(bool p_16bits) { uint16_t size; size = file->get_word(); if (file->eof_reached() || file->get_error()) return true; pat_data = (uint8_t *)CP_ALLOC(4 * ((size >> 2) + 2)); if (!pat_data) return true; source_buffer = (uint32_t *)pat_data; file->get_byte_array((uint8_t *)source_buffer, size); if (file->eof_reached() || file->get_error()) { free_IT_compressed_block(); return true; } source_position = source_buffer; source_remaining_bits = 32; return false; } void CPLoader_IT::free_IT_compressed_block() { if (pat_data) { CP_FREE(pat_data); pat_data = NULL; } } bool CPLoader_IT::load_sample_8bits_IT_compressed(void *p_dest_buffer, int p_buffsize) { int8_t *dest_buffer; /* destination buffer which will be returned */ uint16_t block_length; /* length of compressed data block in samples */ uint16_t block_position; /* position in block */ uint8_t bit_width; /* actual "bit width" */ uint16_t aux_value; /* value read from file to be processed */ int8_t d1, d2; /* integrator buffers (d2 for it2.15) */ int8_t *dest_position; /* position in output buffer */ int8_t v; /* sample value */ bool it215; // is this an it215 module? dest_buffer = (int8_t *)p_dest_buffer; if (dest_buffer == NULL) return true; for (int i = 0; i < p_buffsize; i++) dest_buffer[i] = 0; dest_position = dest_buffer; it215 = (header.cmwt == 0x215); /* now unpack data till the dest buffer is full */ while (p_buffsize) { /* read a new block of compressed data and reset variables */ if (read_IT_compressed_block(false)) { CP_PRINTERR("Out of memory decompressing IT CPSample"); return true; } block_length = (p_buffsize < 0x8000) ? p_buffsize : 0x8000; block_position = 0; bit_width = 9; /* start with width of 9 bits */ d1 = d2 = 0; /* reset integrator buffers */ /* now uncompress the data block */ while (block_position < block_length) { aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */ if (bit_width < 7) { /* method 1 (1-6 bits) */ if (aux_value == (1 << (bit_width - 1))) { /* check for "100..." */ aux_value = read_n_bits_from_IT_compressed_block(3) + 1; /* yes -> read new width; */ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; /* and expand it */ continue; /* ... next value */ } } else if (bit_width < 9) { /* method 2 (7-8 bits) */ uint8_t border = (0xFF >> (9 - bit_width)) - 4; /* lower border for width chg */ if (aux_value > border && aux_value <= (border + 8)) { aux_value -= border; /* convert width to 1-8 */ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; /* and expand it */ continue; /* ... next value */ } } else if (bit_width == 9) { /* method 3 (9 bits) */ if (aux_value & 0x100) { /* bit 8 set? */ bit_width = (aux_value + 1) & 0xff; /* new width... */ continue; /* ... and next value */ } } else { /* illegal width, abort */ free_IT_compressed_block(); CP_PRINTERR("CPSample has illegal BitWidth "); return true; } /* now expand value to signed byte */ if (bit_width < 8) { uint8_t tmp_shift = 8 - bit_width; v = (aux_value << tmp_shift); v >>= tmp_shift; } else v = (int8_t)aux_value; /* integrate upon the sample values */ d1 += v; d2 += d1; /* ... and store it into the buffer */ *(dest_position++) = it215 ? d2 : d1; block_position++; } /* now subtract block lenght from total length and go on */ free_IT_compressed_block(); p_buffsize -= block_length; } return false; } bool CPLoader_IT::load_sample_16bits_IT_compressed(void *p_dest_buffer, int p_buffsize) { int16_t *dest_buffer; /* destination buffer which will be returned */ uint16_t block_length; /* length of compressed data block in samples */ uint16_t block_position; /* position in block */ uint8_t bit_width; /* actual "bit width" */ uint32_t aux_value; /* value read from file to be processed */ int16_t d1, d2; /* integrator buffers (d2 for it2.15) */ int16_t *dest_position; /* position in output buffer */ int16_t v; /* sample value */ bool it215; // is this an it215 module? dest_buffer = (int16_t *)p_dest_buffer; if (dest_buffer == NULL) return true; for (int i = 0; i < p_buffsize; i++) dest_buffer[i] = 0; dest_position = dest_buffer; it215 = (header.cmwt == 0x215); while (p_buffsize) { /* read a new block of compressed data and reset variables */ if (read_IT_compressed_block(true)) { return true; } block_length = (p_buffsize < 0x4000) ? p_buffsize : 0x4000; block_position = 0; bit_width = 17; /* start with width of 9 bits */ d1 = d2 = 0; /* reset integrator buffers */ while (block_position < block_length) { aux_value = read_n_bits_from_IT_compressed_block(bit_width); /* read bits */ if (bit_width < 7) { /* method 1 (1-6 bits) */ if ((signed)aux_value == (1 << (bit_width - 1))) { /* check for "100..." */ aux_value = read_n_bits_from_IT_compressed_block(4) + 1; /* yes -> read new width; */ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; /* and expand it */ continue; /* ... next value */ } } else if (bit_width < 17) { uint16_t border = (0xFFFF >> (17 - bit_width)) - 8; if ((int)aux_value > (int)border && (int)aux_value <= ((int)border + 16)) { aux_value -= border; /* convert width to 1-8 */ bit_width = (aux_value < bit_width) ? aux_value : aux_value + 1; /* and expand it */ continue; /* ... next value */ } } else if (bit_width == 17) { if (aux_value & 0x10000) { /* bit 8 set? */ bit_width = (aux_value + 1) & 0xff; /* new width... */ continue; /* ... and next value */ } } else { /* illegal width, abort */ CP_PRINTERR("CPSample has illegal BitWidth "); free_IT_compressed_block(); return true; } /* now expand value to signed byte */ if (bit_width < 16) { uint8_t tmp_shift = 16 - bit_width; v = (aux_value << tmp_shift); v >>= tmp_shift; } else v = (int16_t)aux_value; /* integrate upon the sample values */ d1 += v; d2 += d1; /* ... and store it into the buffer */ *(dest_position++) = it215 ? d2 : d1; block_position++; } /* now subtract block lenght from total length and go on */ free_IT_compressed_block(); p_buffsize -= block_length; } return false; }