# HG changeset patch # User Michael Pavone # Date 1678933691 25200 # Node ID 6aca1734d5738890684af07d94ce075398adc1b7 # Parent b3832f73444ff4f707e9c2823a60ac8986ff8d1e# Parent 0343f0d5add036adbbb36f84477816ebe75e2e45 Merge diff -r b3832f73444f -r 6aca1734d573 Makefile --- a/Makefile Wed Mar 15 18:50:24 2023 -0700 +++ b/Makefile Wed Mar 15 19:28:11 2023 -0700 @@ -196,7 +196,7 @@ endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o rf5c164.o oscilloscope.o +AUDIOOBJS=ym2612.o psg.o wave.o flac.o vgm.o event_log.o render_audio.o rf5c164.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o @@ -215,9 +215,9 @@ MAINOBJS=blastem.o system.o genesis.o debug.o gdb_remote.o vdp.o $(RENDEROBJS) io.o romdb.o hash.o menu.o xband.o \ realtec.o i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o zip.o bindings.o jcart.o gen_player.o \ - segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o + segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o oscilloscope.o -LIBOBJS=libblastem.o system.o genesis.o debug.o gdb_remote.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ +LIBOBJS=libblastem.o system.o genesis.o vdp.o io.o romdb.o hash.o xband.o realtec.o \ i2c.o nor.o sega_mapper.o multi_game.o megawifi.o $(NET) serialize.o $(TERMINAL) $(CONFIGOBJS) gst.o \ $(M68KOBJS) $(TRANSOBJS) $(AUDIOOBJS) saves.o jcart.o rom.db.o gen_player.o $(LIBZOBJS) \ segacd.o lc8951.o cdimage.o cdd_mcu.o cd_graphics.o cdd_fader.o sft_mapper.o mediaplayer.o diff -r b3832f73444f -r 6aca1734d573 blastem.exe.manifest --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/blastem.exe.manifest Wed Mar 15 19:28:11 2023 -0700 @@ -0,0 +1,15 @@ + + + + + + + + + + + + UTF-8 + + + diff -r b3832f73444f -r 6aca1734d573 blastem.rc --- a/blastem.rc Wed Mar 15 18:50:24 2023 -0700 +++ b/blastem.rc Wed Mar 15 19:28:11 2023 -0700 @@ -1,6 +1,6 @@ 1 VERSIONINFO -FILEVERSION 0,4,0,0 -PRODUCTVERSION 0,4,0,0 +FILEVERSION 0,6,3,0 +PRODUCTVERSION 0,6,3,0 BEGIN BLOCK "StringFileInfo" BEGIN @@ -10,10 +10,10 @@ VALUE "FileDescription", "BlastEm - A fast and accurate Genesis/Megadrive emulator" VALUE "FileVersion", "1.0" VALUE "InternalName", "blastem" - VALUE "LegalCopyright", "Copyright 2011-2016 Michael Pavone" + VALUE "LegalCopyright", "Copyright 2011-2023 Michael Pavone" VALUE "OriginalFilename", "blastem.exe" VALUE "ProductName", "BlastEm" - VALUE "ProductVersion", "0.4.0" + VALUE "ProductVersion", "0.6.3-pre" END END @@ -23,3 +23,4 @@ END END 2 ICON "icons/windows.ico" +3 RTF_MANIFEST blastem.exe.manifest diff -r b3832f73444f -r 6aca1734d573 cdimage.c --- a/cdimage.c Wed Mar 15 18:50:24 2023 -0700 +++ b/cdimage.c Wed Mar 15 19:28:11 2023 -0700 @@ -111,17 +111,21 @@ if (track) { lba -= media->tracks[track - 1].end_lba; } - if (media->tracks[track].has_subcodes) { - if (!media->tmp_buffer) { - media->tmp_buffer = calloc(1, 96); + if (media->tracks[track].flac) { + flac_seek(media->tracks[track].flac, (media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes) / 4); + } else { + if (media->tracks[track].has_subcodes) { + if (!media->tmp_buffer) { + media->tmp_buffer = calloc(1, 96); + } + fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); + int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); + if (bytes != 96) { + fprintf(stderr, "Only read %d subcode bytes\n", bytes); + } } - fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); - int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); - if (bytes != 96) { - fprintf(stderr, "Only read %d subcode bytes\n", bytes); - } + fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); } - fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); } } return track; @@ -156,12 +160,23 @@ return 0; } else if ((media->tracks[media->cur_track].sector_bytes < 2352 && offset < 16) || offset > (media->tracks[media->cur_track].sector_bytes + 16)) { return fake_read(media->cur_sector, offset); + } else if (media->tracks[media->cur_track].flac) { + if (offset & 3) { + return media->byte_storage[(offset & 3) - 1]; + } else { + int16_t samples[2]; + flac_get_sample(media->tracks[media->cur_track].flac, samples, 2); + media->byte_storage[0] = samples[0] >> 8; + media->byte_storage[1] = samples[1]; + media->byte_storage[2] = samples[1] >> 8; + return samples[0]; + } } else { if (media->tracks[media->cur_track].need_swap) { if (offset & 1) { - return media->byte_storage; + return media->byte_storage[0]; } - media->byte_storage = fgetc(media->tracks[media->cur_track].f); + media->byte_storage[0] = fgetc(media->tracks[media->cur_track].f); } return fgetc(media->tracks[media->cur_track].f); } @@ -198,6 +213,7 @@ int track = -1; uint8_t audio_byte_swap = 0; FILE *f = NULL; + flac_file *flac = NULL; int track_of_file = -1; uint8_t has_index_0 = 0; uint32_t extra_offset = 0; @@ -215,6 +231,7 @@ warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); } tracks[track].f = f; + tracks[track].flac = flac; cmd = cmd_start(end); @@ -259,11 +276,12 @@ memcpy(fname + dirlen + 1, cmd, end-cmd); fname[dirlen + 1 + (end-cmd)] = 0; } + flac = NULL; f = fopen(fname, "rb"); if (!f) { fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); } - free(fname); + track_of_file = -1; for (end++; *end && *end != '\n' && *end != '\r'; end++) { @@ -276,19 +294,29 @@ } else if (startswith(end, "WAVE")) { audio_byte_swap = 0; wave_header wave; - if (!wave_read_header(f, &wave)) { - fatal_error("Wave file %s specified by cute sheet %s.%s is not valid\n", fname, media->name, media->extension); + if (wave_read_header(f, &wave)) { + if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { + warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); + } + extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); + } else { + fseek(f, 0, SEEK_SET); + flac = flac_file_from_file(f); + if (!flac) { + fatal_error("WAVE file %s in cue sheet %s.%s is neither a valid WAVE nor a valid FLAC file\n", fname, media->name, media->extension); + } + if (flac->sample_rate != 44100 || flac->bits_per_sample != 16 || flac->channels != 2) { + warning("FLAC files in a CUE sheet should match CD audio specs, %s does not\n", fname); + } + } - if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { - warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); - } - extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); } else { warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); } break; } } + free(fname); } } } else if (track >= 0) { @@ -344,10 +372,22 @@ for (int track = 0; track < media->num_tracks; track++) { if (track == media->num_tracks - 1 && tracks[track].f) { uint32_t start_lba =tracks[track].fake_pregap ? tracks[track].start_lba : tracks[track].pregap_lba; - tracks[track].end_lba = start_lba + (file_size(tracks[track].f) - tracks[track].file_offset)/ tracks[track].sector_bytes; + uint32_t fsize; + if (tracks[track].flac) { + fsize = tracks[track].flac->total_samples * 4; + } else { + fsize = file_size(tracks[track].f); + } + tracks[track].end_lba = start_lba + (fsize - tracks[track].file_offset)/ tracks[track].sector_bytes; } else if (tracks[track].f != f) { uint32_t start_lba =tracks[track-1].fake_pregap ? tracks[track-1].start_lba : tracks[track-1].pregap_lba; - tracks[track-1].end_lba = start_lba + (file_size(tracks[track-1].f) - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; + uint32_t fsize; + if (tracks[track-1].flac) { + fsize = tracks[track-1].flac->total_samples * 4; + } else { + fsize = file_size(tracks[track-1].f); + } + tracks[track-1].end_lba = start_lba + (fsize - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; offset = tracks[track-1].end_lba; } if (!tracks[track].fake_pregap) { @@ -359,7 +399,7 @@ //replace cue sheet with first sector free(media->buffer); media->buffer = calloc(2048, 1); - if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { + if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352 && !tracks[0].flac) { // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector uint8_t msf[3]; fseek(tracks[0].f, 12, SEEK_SET); @@ -588,10 +628,12 @@ save_int32(buf, 0); } save_int8(buf, media->in_fake_pregap); - save_int8(buf, media->byte_storage); + save_int8(buf, media->byte_storage[0]); if (media->tmp_buffer) { save_buffer8(buf, media->tmp_buffer, 96); } + save_int8(buf, media->byte_storage[1]); + save_int8(buf, media->byte_storage[2]); } void cdimage_deserialize(deserialize_buffer *buf, void *vmedia) @@ -607,8 +649,12 @@ fseek(media->tracks[media->cur_track].f, seekpos, SEEK_SET); } media->in_fake_pregap = load_int8(buf); - media->byte_storage = load_int8(buf); + media->byte_storage[0] = load_int8(buf); if (media->tmp_buffer) { load_buffer8(buf, media->tmp_buffer, 96); } + if (buf->size - buf->cur_pos >= 2) { + media->byte_storage[1] = load_int8(buf); + media->byte_storage[2] = load_int8(buf); + } } diff -r b3832f73444f -r 6aca1734d573 debug.c --- a/debug.c Wed Mar 15 18:50:24 2023 -0700 +++ b/debug.c Wed Mar 15 19:28:11 2023 -0700 @@ -1055,7 +1055,9 @@ #endif do { process_events(); +#ifndef IS_LIB render_update_display(); +#endif #ifndef _WIN32 timeout.tv_sec = 0; timeout.tv_usec = 16667; diff -r b3832f73444f -r 6aca1734d573 flac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flac.c Wed Mar 15 19:28:11 2023 -0700 @@ -0,0 +1,657 @@ +#include +#include +#include +#include "flac.h" + +static uint8_t read_byte_buffer(flac_file *f) +{ + if (f->offset >= f->buffer_size) { + return 0; + } + uint8_t *buf = f->read_data; + return buf[f->offset++]; +} + +static void seek_buffer(flac_file *f, uint32_t offset, uint8_t relative) +{ + f->offset = relative ? f->offset + offset : offset; +} + +static uint32_t tell_buffer(flac_file *f) +{ + return f->offset; +} + +static uint8_t read_byte_file(flac_file *f) +{ + int result = fgetc(f->read_data); + if (result == EOF) { + return 0; + } + return result; +} + +static void seek_file(flac_file *f, uint32_t offset, uint8_t relative) +{ + fseek(f->read_data, offset, relative ? SEEK_CUR : SEEK_SET); +} + +static uint32_t tell_file(flac_file *f) +{ + return ftell(f->read_data); +} + +static void read_chars(flac_file *f, char *dest, uint32_t count) +{ + for (; count > 0; --count) + { + *(dest++) = f->read_byte(f); + } +} + +static uint16_t read16(flac_file *f) +{ + uint16_t ret = f->read_byte(f) << 8; + ret |= f->read_byte(f); + return ret; +} + +static uint64_t read64(flac_file *f) +{ + uint64_t value = ((uint64_t)f->read_byte(f)) << 56; + value |= ((uint64_t)f->read_byte(f)) << 48; + value |= ((uint64_t)f->read_byte(f)) << 40; + value |= ((uint64_t)f->read_byte(f)) << 32; + value |= ((uint64_t)f->read_byte(f)) << 24; + value |= f->read_byte(f) << 16; + value |= f->read_byte(f) << 8; + value |= f->read_byte(f); + return value; +} + +static uint32_t read_bits(flac_file *f, uint32_t num_bits) +{ + uint32_t ret = 0; + while (num_bits) + { + if (!f->bits) { + f->cur_byte = f->read_byte(f); + f->bits = 8; + } + uint32_t new_bits = f->bits; + if (new_bits > num_bits) { + new_bits = num_bits; + } + ret <<= new_bits; + uint32_t mask = (1 << new_bits) - 1; + ret |= (f->cur_byte >> (f->bits - new_bits)) & mask; + f->bits -= new_bits; + num_bits -= new_bits; + } + return ret; +} + +static uint64_t read_bits64(flac_file *f, uint64_t num_bits) +{ + uint64_t ret = 0; + while (num_bits) + { + if (!f->bits) { + f->cur_byte = f->read_byte(f); + f->bits = 8; + } + uint64_t new_bits = f->bits; + if (new_bits > num_bits) { + new_bits = num_bits; + } + ret <<= new_bits; + uint64_t mask = (1 << new_bits) - 1; + ret |= f->cur_byte & mask; + f->cur_byte >>= new_bits; + f->bits -= new_bits; + num_bits -= new_bits; + } + return ret; +} + +typedef struct { + uint32_t size; + uint8_t type; + uint8_t is_last; +} meta_block_header; + +enum { + STREAMINFO, + PADDING, + APPLICATION, + SEEKTABLE, + VORBIS_COMMENT, + CUESHEET, + PICTURE +}; + +static void read_meta_block_header(flac_file *f, meta_block_header *dest) +{ + dest->is_last = read_bits(f, 1); + dest->type = read_bits(f, 7); + dest->size = read_bits(f, 24); +} + +static void parse_streaminfo(flac_file *f) +{ + read16(f);//min block size + read16(f);//max block size + read_bits(f, 24);//min frame size + read_bits(f, 24);//max frame size + f->sample_rate = read_bits(f, 20); + f->channels = read_bits(f, 3) + 1; + f->bits_per_sample = read_bits(f, 5) + 1; + f->total_samples = read_bits64(f, 36); + f->seek(f, 16, 1);//MD5 +} + +static void parse_seektable(flac_file *f, uint32_t size) +{ + f->num_seekpoints = size / 18; + f->seekpoints = calloc(f->num_seekpoints, sizeof(flac_seekpoint)); + for (uint32_t i = 0; i < f->num_seekpoints; i++) + { + f->seekpoints[i].sample_number = read64(f); + f->seekpoints[i].offset = read64(f); + f->seekpoints[i].sample_count = read16(f); + } +} + +static uint8_t parse_header(flac_file *f) +{ + char id[4]; + read_chars(f, id, sizeof(id)); + if (memcmp("fLaC", id, sizeof(id))) { + return 0; + } + meta_block_header header; + do { + read_meta_block_header(f, &header); + if (header.type == STREAMINFO) { + parse_streaminfo(f); + } else if (header.type == SEEKTABLE) { + parse_seektable(f, header.size); + } else { + f->seek(f, header.size, 1); + } + } while (!header.is_last); + f->first_frame_offset = f->tell(f); + return 1; +} + +flac_file *flac_file_from_buffer(void *buffer, uint32_t size) +{ + flac_file *f = calloc(1, sizeof(flac_file)); + f->read_data = buffer; + f->read_byte = read_byte_buffer; + f->seek = seek_buffer; + f->tell = tell_buffer; + f->buffer_size = size; + if (parse_header(f)) { + return f; + } + free(f); + return NULL; +} + +flac_file *flac_file_from_file(FILE *file) +{ + flac_file *f = calloc(1, sizeof(flac_file)); + f->read_data = file; + f->read_byte = read_byte_file; + f->seek = seek_file; + f->tell = tell_file; + if (parse_header(f)) { + return f; + } + free(f); + return NULL; +} + +static uint64_t read_utf64(flac_file *f) +{ + uint8_t byte = f->read_byte(f); + if (!(byte & 0x80)) { + return byte; + } + uint8_t mask = 0x40; + uint8_t length = 0; + while (byte & mask) + { + mask >>= 1; + length++; + } + uint64_t value = byte & (mask - 1); + for (uint8_t i = 0; i < length; i++) + { + value <<= 6; + value |= f->read_byte(f) & 0x3F; + } + return value; +} + +static uint32_t read_utf32(flac_file *f) +{ + uint8_t byte = f->read_byte(f); + if (!(byte & 0x80)) { + return byte; + } + uint8_t mask = 0x40; + uint8_t length = 0; + while (byte & mask) + { + mask >>= 1; + length++; + } + uint32_t value = byte & (mask - 1); + for (uint8_t i = 0; i < length; i++) + { + value <<= 6; + value |= f->read_byte(f) & 0x3F; + } + return value; +} + +static uint8_t parse_frame_header(flac_file *f) +{ + uint16_t sync = read_bits(f, 14); + if (sync != 0x3FFE) { + fprintf(stderr, "Invalid sync FLAC sync pattern: %X\n", sync); + return 0; + } + read_bits(f, 1);//reserved + uint8_t block_size_strategy = read_bits(f, 1); + uint8_t block_size_code = read_bits(f, 4); + uint8_t sample_rate_code = read_bits(f, 4); + uint8_t channels = read_bits(f, 4); + uint8_t joint_stereo = 0; + if (channels > 7) { + joint_stereo = (channels & 7) + 1; + channels = 2; + } else { + ++channels; + } + f->frame_channels = channels; + f->frame_joint_stereo = joint_stereo; + uint8_t bits_per_sample_code = read_bits(f, 3); + if (!bits_per_sample_code) { + f->frame_bits_per_sample = f->bits_per_sample; + } else if (bits_per_sample_code < 3) { + f->frame_bits_per_sample = 4 + 4 * bits_per_sample_code; + } else { + f->frame_bits_per_sample = 4 * bits_per_sample_code; + } + read_bits(f, 1);//reserved + uint32_t block_num; + if (block_size_strategy) { + f->frame_start_sample = read_utf64(f); + } else { + block_num = read_utf32(f); + } + uint32_t block_size = 0; + switch (block_size_code) + { + case 0: + fputs("Detected reserved block size 0", stderr); + return 0; + case 1: + block_size = 192; + break; + case 6: + block_size = f->read_byte(f) + 1; + break; + case 7: + block_size = read16(f) + 1; + break; + default: + if (block_size_code < 8) { + block_size = 576 * (1 << (block_size_code - 2)); + } else { + block_size = 256 * (1 << (block_size_code - 8)); + } + break; + } + f->frame_block_size = block_size; + if (!block_size_strategy) { + f->frame_start_sample = ((uint64_t)block_num) * ((uint64_t)block_size); + } + uint32_t sample_rate; + switch (sample_rate_code) + { + case 15: + fputs("Invalid frame header sample rate", stderr); + case 0: + sample_rate = f->sample_rate; + break; + case 1: + sample_rate = 88200; + break; + case 2: + sample_rate = 176400; + break; + case 3: + sample_rate = 192000; + break; + case 4: + sample_rate = 8000; + break; + case 5: + sample_rate = 16000; + break; + case 6: + sample_rate = 22050; + break; + case 7: + sample_rate = 44100; + break; + case 8: + sample_rate = 32000; + break; + case 9: + sample_rate = 44100; + break; + case 10: + sample_rate = 48000; + break; + case 11: + sample_rate = 96000; + break; + case 12: + sample_rate = f->read_byte(f) * 1000; + break; + case 13: + sample_rate = read16(f); + break; + case 14: + sample_rate = read16(f) * 10; + break; + } + f->frame_sample_rate = sample_rate; + f->read_byte(f);//CRC-8 + return 1; +} + +enum { + SUBFRAME_CONSTANT, + SUBFRAME_VERBATIM, + SUBFRAME_FIXED = 8, + SUBFRAME_LPC = 0x20, +}; + +static int32_t sign_extend(uint32_t value, uint32_t bits) +{ + if (value & (1 << (bits - 1))) { + value |= ~((1 << bits) - 1); + } + return value; +} + +static int32_t signed_sample(uint32_t sample_bits, uint32_t sample, uint8_t wasted_bits) +{ + sample <<= wasted_bits; + sample = sign_extend(sample, sample_bits); + return sample; +} + +static void decode_residuals(flac_file *f, flac_subframe *sub, int64_t *coefficients, uint32_t order, int64_t shift) +{ + uint8_t residual_method = read_bits(f, 2); + uint8_t rice_param_bits = residual_method ? 5 : 4; + uint32_t partition_count = 1 << read_bits(f, 4); + uint32_t cur = order; + uint32_t partition_size = f->frame_block_size / partition_count; + for (uint32_t partition = 0; partition < partition_count; partition++) + { + uint32_t rice_param = read_bits(f, rice_param_bits); + if (rice_param == (1 << rice_param_bits) - 1) { + //escape code, residuals are unencoded + rice_param = read_bits(f, rice_param_bits); + for (uint32_t end = partition ? cur + partition_size : partition_size; cur < end; cur++) + { + int64_t prediction = 0; + for (uint32_t i = 0; i < order; i++) + { + prediction += ((int64_t)sub->decoded[cur - 1 - i]) * coefficients[i]; + } + if (shift) { + prediction >>= shift; + } + prediction += sign_extend(read_bits(f, rice_param), rice_param); + sub->decoded[cur] = prediction; + } + } else { + for (uint32_t end = partition ? cur + partition_size : partition_size; cur < end; cur++) + { + int64_t prediction = 0; + for (uint32_t i = 0; i < order; i++) + { + prediction += ((int64_t)sub->decoded[cur - 1 - i]) * coefficients[i]; + } + if (shift) { + prediction >>= shift; + } + uint32_t residual = 0; + while (!read_bits(f, 1)) + { + ++residual; + } + residual <<= rice_param; + residual |= read_bits(f, rice_param); + if (residual & 1) { + sub->decoded[cur] = prediction - (residual >> 1) - 1; + } else { + sub->decoded[cur] = prediction + (residual >> 1); + } + } + } + } +} + +static void decode_subframe(flac_file *f, flac_subframe *sub) +{ + if (f->frame_block_size > sub->allocated_samples) { + sub->decoded = realloc(sub->decoded, sizeof(int32_t) * f->frame_block_size); + sub->allocated_samples = f->frame_block_size ; + } + int64_t prediction_coefficients[32]; + read_bits(f, 1);//reserved + uint8_t type = read_bits(f, 6); + uint8_t has_wasted_bits = read_bits(f, 1); + uint8_t wasted_bits = 0; + if (has_wasted_bits) { + ++wasted_bits; + while (!read_bits(f, 1)) + { + ++wasted_bits; + } + } + uint32_t sample_bits = f->frame_bits_per_sample - wasted_bits; + if (f->frame_joint_stereo) { + int channel = sub - f->subframes; + if (f->frame_joint_stereo == 2 && !channel || (channel && f->frame_joint_stereo != 2)) { + sample_bits++; + } + } + if (type == SUBFRAME_CONSTANT) { + int32_t sample = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + for (uint32_t i = 0; i < f->frame_block_size; i++) + { + sub->decoded[i] = sample; + } + } else if (type == SUBFRAME_VERBATIM) { + for (uint32_t i = 0; i < f->frame_block_size; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + } else if (type & SUBFRAME_LPC) { + uint32_t order = (type & 0x1F) + 1; + for (uint32_t i = 0; i < order; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + uint32_t coefficient_bits = read_bits(f, 4) + 1; + int64_t shift_bits = read_bits(f, 5); + for (uint32_t i = 0; i < order; i++) + { + prediction_coefficients[i] = sign_extend(read_bits(f, coefficient_bits), coefficient_bits); + } + decode_residuals(f, sub, prediction_coefficients, order, shift_bits); + } else if (type & SUBFRAME_FIXED) { + uint32_t order = type & 7; + for (uint32_t i = 0; i < order; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + switch (order) + { + case 1: + prediction_coefficients[0] = 1; + break; + case 2: + prediction_coefficients[0] = 2; + prediction_coefficients[1] = -1; + break; + case 3: + prediction_coefficients[0] = 3; + prediction_coefficients[1] = -3; + prediction_coefficients[2] = 1; + break; + case 4: + prediction_coefficients[0] = 4; + prediction_coefficients[1] = -6; + prediction_coefficients[2] = 4; + prediction_coefficients[3] = -1; + break; + } + decode_residuals(f, sub, prediction_coefficients, order, 0); + } else { + fprintf(stderr, "Invalid subframe type %X\n", type); + } +} + +static uint8_t decode_frame(flac_file *f) +{ + if (!parse_frame_header(f)) { + return 0; + } + if (f->frame_channels > f->subframe_alloc) { + f->subframes = realloc(f->subframes, sizeof(flac_subframe) * f->frame_channels); + memset(f->subframes + f->subframe_alloc, 0, sizeof(flac_subframe) * (f->frame_channels - f->subframe_alloc)); + f->subframe_alloc = f->frame_channels; + } + for (uint8_t channel = 0; channel < f->frame_channels; channel++) + { + decode_subframe(f, f->subframes + channel); + } + f->bits = 0; + read16(f);//Frame footer CRC-16 + f->frame_sample_pos = 0; + return 1; +} + +uint8_t flac_get_sample(flac_file *f, int16_t *out, uint8_t desired_channels) +{ + if (f->frame_sample_pos == f->frame_block_size) { + if (!decode_frame(f)) { + return 0; + } + } + uint8_t copy_channels; + if (f->frame_channels == 1 && desired_channels > 1) { + int16_t sample = f->subframes->decoded[f->frame_sample_pos]; + *(out++) = sample; + *(out++) = sample; + copy_channels = 2; + } else { + int32_t left, right, mid, diff; + switch (f->frame_joint_stereo) + { + case 0: + copy_channels = desired_channels; + if (copy_channels > f->frame_channels) { + copy_channels = f->frame_channels; + } + for (uint8_t i = 0; i < copy_channels; i++) + { + *(out++) = f->subframes[i].decoded[f->frame_sample_pos]; + } + break; + case 1: + //left-side + copy_channels = 2; + *(out++) = left = f->subframes[0].decoded[f->frame_sample_pos]; + if (desired_channels > 1) { + *(out++) = left + f->subframes[1].decoded[f->frame_sample_pos]; + } + break; + case 2: + //side-right + copy_channels = 2; + right = f->subframes[1].decoded[f->frame_sample_pos]; + left = right + f->subframes[0].decoded[f->frame_sample_pos]; + *(out++) = left; + if (desired_channels > 1) { + *(out++) = right; + } + break; + case 3: + //mid-side + copy_channels = 2; + mid = f->subframes[0].decoded[f->frame_sample_pos]; + diff = f->subframes[1].decoded[f->frame_sample_pos]; + left = (diff + 2 * mid) >> 1; + *(out++) = left; + if (desired_channels > 1) { + *(out++) = left - diff; + } + break; + } + } + for (uint8_t i = copy_channels; i < desired_channels; i++) + { + *(out++) = 0; + } + f->frame_sample_pos++; + + return 1; +} + +void flac_seek(flac_file *f, uint64_t sample_number) +{ + if (sample_number >= f->frame_start_sample && sample_number < f->frame_start_sample + f->frame_block_size) { + f->frame_sample_pos = sample_number - f->frame_start_sample; + return; + } + uint32_t best_seekpoint = f->num_seekpoints + 1; + if (f->num_seekpoints) { + uint64_t best_diff; + for (uint32_t i = 0; i < f->num_seekpoints; i++) + { + if (f->seekpoints[i].sample_number > sample_number) { + continue; + } + uint64_t diff = sample_number - f->seekpoints[i].sample_number; + if (best_seekpoint > f->num_seekpoints || diff < best_diff) { + best_seekpoint = i; + best_diff = diff; + } + } + } + //TODO: more efficient seeking + if (best_seekpoint > f->num_seekpoints) { + f->seek(f, f->first_frame_offset, 0); + } else if (f->seekpoints[best_seekpoint].sample_number > f->frame_start_sample || f->frame_start_sample > sample_number){ + f->seek(f, f->seekpoints[best_seekpoint].offset + f->first_frame_offset, 0); + } + do { + if (!decode_frame(f)) { + return; + } + } while ((f->frame_start_sample + f->frame_block_size) <= sample_number); + f->frame_sample_pos = sample_number - f->frame_start_sample; +} diff -r b3832f73444f -r 6aca1734d573 flac.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flac.h Wed Mar 15 19:28:11 2023 -0700 @@ -0,0 +1,59 @@ +#ifndef FLAC_H_ +#define FLAC_H_ + +#include +#include + +typedef struct flac_file flac_file; + +typedef uint8_t (*flac_read_fun)(flac_file *f); +typedef void (*flac_seek_fun)(flac_file *f, uint32_t offset, uint8_t relative); +typedef uint32_t (*flac_tell_fun)(flac_file *f); + +typedef struct { + uint32_t allocated_samples; + int32_t *decoded; +} flac_subframe; + +typedef struct { + uint64_t sample_number; + uint64_t offset; + uint16_t sample_count; +} flac_seekpoint; + +struct flac_file { + uint64_t total_samples; + uint64_t frame_start_sample; + void *read_data; + flac_read_fun read_byte; + flac_seek_fun seek; + flac_tell_fun tell; + flac_subframe *subframes; + flac_seekpoint *seekpoints; + uint32_t num_seekpoints; + uint32_t offset; + uint32_t buffer_size; + uint32_t first_frame_offset; + + uint32_t frame_sample_pos; + + uint32_t sample_rate; + uint32_t frame_sample_rate; + uint32_t frame_block_size; + uint8_t bits_per_sample; + uint8_t frame_bits_per_sample; + uint8_t channels; + uint8_t frame_channels; + uint8_t frame_joint_stereo; + uint8_t subframe_alloc; + + uint8_t cur_byte; + uint8_t bits; +}; + +flac_file *flac_file_from_buffer(void *buffer, uint32_t size); +flac_file *flac_file_from_file(FILE *file); +uint8_t flac_get_sample(flac_file *f, int16_t *out, uint8_t desired_channels); +void flac_seek(flac_file *f, uint64_t sample_number); + +#endif //FLAC_H_ diff -r b3832f73444f -r 6aca1734d573 genesis.c --- a/genesis.c Wed Mar 15 18:50:24 2023 -0700 +++ b/genesis.c Wed Mar 15 19:28:11 2023 -0700 @@ -418,7 +418,9 @@ z80_run(z_context, z_context->current_cycle + 4); } gen->enter_z80_debugger = 0; +#ifndef IS_LIB zdebugger(z_context, z_context->pc); +#endif } z80_run(z_context, mclks); } else @@ -485,9 +487,11 @@ gen->reset_cycle = CYCLE_NEVER; } if (v_context->frame != gen->last_frame) { +#ifndef IS_LIB if (gen->ym->scope) { scope_render(gen->ym->scope); } +#endif //printf("reached frame end %d | MCLK Cycles: %d, Target: %d, VDP cycles: %d, vcounter: %d, hslot: %d\n", gen->last_frame, mclks, gen->frame_end, v_context->cycles, v_context->vcounter, v_context->hslot); uint32_t elapsed = v_context->frame - gen->last_frame; gen->last_frame = v_context->frame; @@ -556,11 +560,13 @@ if (address) { if (gen->header.enter_debugger) { gen->header.enter_debugger = 0; +#ifndef IS_LIB if (gen->header.debugger_type == DEBUGGER_NATIVE) { debugger(context, address); } else { gdb_debug_enter(context, address); } +#endif } #ifdef NEW_CORE if (gen->header.save_state) { @@ -1483,16 +1489,20 @@ printf("Loaded %s\n", statefile); if (gen->header.enter_debugger) { gen->header.enter_debugger = 0; +#ifndef IS_LIB insert_breakpoint(gen->m68k, pc, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter); +#endif } adjust_int_cycle(gen->m68k, gen->vdp); start_68k_context(gen->m68k, pc); } else { if (gen->header.enter_debugger) { gen->header.enter_debugger = 0; +#ifndef IS_LIB uint32_t address = read_word(4, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k) << 16 | read_word(6, (void **)gen->m68k->mem_pointers, &gen->m68k->options->gen, gen->m68k); insert_breakpoint(gen->m68k, address, gen->header.debugger_type == DEBUGGER_NATIVE ? debugger : gdb_debug_enter); +#endif } m68k_reset(gen->m68k); } @@ -1773,6 +1783,7 @@ static void toggle_debug_view(system_header *system, uint8_t debug_view) { +#ifndef IS_LIB genesis_context *gen = (genesis_context *)system; if (debug_view < DEBUG_OSCILLOSCOPE) { vdp_toggle_debug_view(gen->vdp, debug_view); @@ -1798,6 +1809,7 @@ } else if (debug_view == DEBUG_CD_GRAPHICS && gen->expansion) { scd_toggle_graphics_debug(gen->expansion); } +#endif } static void *tmss_rom_write_16(uint32_t address, void *context, uint16_t value) diff -r b3832f73444f -r 6aca1734d573 libblastem.c --- a/libblastem.c Wed Mar 15 18:50:24 2023 -0700 +++ b/libblastem.c Wed Mar 15 19:28:11 2023 -0700 @@ -77,7 +77,11 @@ tern_node *config; uint8_t use_native_states = 1; system_header *current_system; -system_media media; +static system_media media; +const system_media *current_media(void) +{ + return &media; +} RETRO_API void retro_init(void) { @@ -238,10 +242,10 @@ media.size = game->size; stype = detect_system_type(&media); current_system = alloc_config_system(stype, &media, 0, 0); - + unsigned format = RETRO_PIXEL_FORMAT_XRGB8888; retro_environment(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &format); - + return current_system != NULL; } diff -r b3832f73444f -r 6aca1734d573 mediaplayer.c --- a/mediaplayer.c Wed Mar 15 18:50:24 2023 -0700 +++ b/mediaplayer.c Wed Mar 15 19:28:11 2023 -0700 @@ -443,7 +443,16 @@ void flac_frame(media_player *player) { - render_sleep_ms(15); + for (uint32_t remaining_samples = player->flac->sample_rate / 60; remaining_samples > 0; remaining_samples--) + { + int16_t samples[2]; + if (flac_get_sample(player->flac, samples, 2)) { + render_put_stereo_sample(player->audio, samples[0], samples[1]); + } else { + player->state = STATE_PAUSED; + return; + } + } } void vgm_init(media_player *player, uint32_t opts) @@ -571,6 +580,14 @@ free(player->wave); } +static void flac_player_init(media_player *player) +{ + player->flac = flac_file_from_buffer(player->media->buffer, player->media->size); + if (player->flac) { + player->audio = render_audio_source("Audio File", player->flac->sample_rate, 1, 2); + } +} + static void resume_player(system_header *system) { media_player *player = (media_player *)system; @@ -594,10 +611,15 @@ } break; case STATE_PAUSED: +#ifndef IS_LIB render_sleep_ms(15); +#endif break; } + //TODO: Fix this for libretro build properly +#ifndef IS_LIB render_update_display(); +#endif } } @@ -689,6 +711,9 @@ case AUDIO_WAVE: wave_player_init(player); break; + case AUDIO_FLAC: + flac_player_init(player); + break; } return player; diff -r b3832f73444f -r 6aca1734d573 mediaplayer.h --- a/mediaplayer.h Wed Mar 15 18:50:24 2023 -0700 +++ b/mediaplayer.h Wed Mar 15 19:28:11 2023 -0700 @@ -5,6 +5,7 @@ #include "system.h" #include "vgm.h" #include "wave.h" +#include "flac.h" #include "render_audio.h" typedef struct chip_info chip_info; @@ -28,6 +29,7 @@ vgm_extended_header *vgm_ext; data_block *ym_seek_block; wave_header *wave; + flac_file *flac; audio_source *audio; chip_info *chips; uint32_t num_chips; diff -r b3832f73444f -r 6aca1734d573 nuklear_ui/blastem_nuklear.c --- a/nuklear_ui/blastem_nuklear.c Wed Mar 15 18:50:24 2023 -0700 +++ b/nuklear_ui/blastem_nuklear.c Wed Mar 15 19:28:11 2023 -0700 @@ -927,17 +927,15 @@ memcpy(button_key + pad_key_size, button_base, sizeof(button_base)); char *final_key; - for (int i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i++) + for (int i = 0; i <= SDL_CONTROLLER_BUTTON_MAX; i++) { char *base; const char *suffix; size_t base_key_len; - if ( i < SDL_CONTROLLER_BUTTON_DPAD_UP) { + if ( i < SDL_CONTROLLER_BUTTON_DPAD_UP || i > SDL_CONTROLLER_BUTTON_DPAD_RIGHT) { suffix = SDL_GameControllerGetStringForButton(i); base_key_len = button_key_size; base = button_key; - - } else { static const char *dir_keys[] = {"up", "down", "left", "right"}; suffix = dir_keys[i - SDL_CONTROLLER_BUTTON_DPAD_UP]; diff -r b3832f73444f -r 6aca1734d573 oscilloscope.c --- a/oscilloscope.c Wed Mar 15 18:50:24 2023 -0700 +++ b/oscilloscope.c Wed Mar 15 19:28:11 2023 -0700 @@ -1,6 +1,8 @@ #include "oscilloscope.h" #include "render.h" #include "blastem.h" +#include +#include #define INVALID_TRIGGER 0xFFFFFFFF #define WIDTH 1280 diff -r b3832f73444f -r 6aca1734d573 psg.c --- a/psg.c Wed Mar 15 18:50:24 2023 -0700 +++ b/psg.c Wed Mar 15 19:28:11 2023 -0700 @@ -23,6 +23,7 @@ void psg_enable_scope(psg_context *context, oscilloscope *scope, uint32_t master_clock) { +#ifndef IS_LIB context->scope = scope; static const char *names[] = { "PSG #1", @@ -34,6 +35,7 @@ { context->scope_channel[i] = scope_add_channel(scope, names[i], master_clock / context->clock_inc); } +#endif } void psg_free(psg_context *context) @@ -146,9 +148,11 @@ } else { value = 0; } +#ifndef IS_LIB if (context->scope) { scope_add_sample(context->scope, context->scope_channel[i], value, trigger[i]); } +#endif } value = 0; if (context->noise_out) { @@ -160,9 +164,11 @@ right_accum += value; } } +#ifndef IS_LIB if (context->scope) { scope_add_sample(context->scope, context->scope_channel[3], value, trigger[3]); } +#endif render_put_stereo_sample(context->audio, left_accum, right_accum); diff -r b3832f73444f -r 6aca1734d573 rf5c164.c --- a/rf5c164.c Wed Mar 15 18:50:24 2023 -0700 +++ b/rf5c164.c Wed Mar 15 19:28:11 2023 -0700 @@ -154,14 +154,20 @@ int16_t left = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] >> 4)) >> 5; int16_t right = (sample * (pcm->channels[pcm->cur_channel].regs[PAN] & 0xF)) >> 5; //printf("chan %d, raw %X, sample %d, left %d, right %d, ptr %X (raw %X)\n", pcm->cur_channel, pcm->channels[pcm->cur_channel].sample, sample, left, right, pcm->channels[pcm->cur_channel].cur_ptr >> 11, pcm->channels[pcm->cur_channel].cur_ptr); +#ifndef IS_LIB if (pcm->scope) { scope_add_sample(pcm->scope, pcm->channels[pcm->cur_channel].scope_channel, sample, pcm->channels[pcm->cur_channel].trigger); } +#endif pcm->left += left; pcm->right += right; +#ifdef IS_LIB + } +#else } else if (pcm->scope) { scope_add_sample(pcm->scope, pcm->channels[pcm->cur_channel].scope_channel, 0, 0); } +#endif write_if_not_sounding(pcm); CHECK; case 10: @@ -246,6 +252,7 @@ void rf5c164_enable_scope(rf5c164* pcm, oscilloscope *scope) { +#ifndef IS_LIB static const char *names[] = { "Richo #1", "Richo #2", @@ -261,6 +268,7 @@ { pcm->channels[i].scope_channel = scope_add_channel(scope, names[i], 50000000 / (pcm->clock_step * 96)); } +#endif } void rf5c164_serialize(rf5c164* pcm, serialize_buffer *buf) diff -r b3832f73444f -r 6aca1734d573 rom.db --- a/rom.db Wed Mar 15 18:50:24 2023 -0700 +++ b/rom.db Wed Mar 15 19:28:11 2023 -0700 @@ -1530,3 +1530,9 @@ } } } +c8a3667631fdbd4d0073e42ada9f7199d09c0cfa { + # European version of this game specifies JU in header + # but is not entirely 60Hz tolerant + name Alisia Dragoon + regions E +} diff -r b3832f73444f -r 6aca1734d573 segacd.c --- a/segacd.c Wed Mar 15 18:50:24 2023 -0700 +++ b/segacd.c Wed Mar 15 19:28:11 2023 -0700 @@ -1136,11 +1136,13 @@ if (cd->enter_debugger) { genesis_context *gen = cd->genesis; cd->enter_debugger = 0; +#ifndef IS_LIB if (gen->header.debugger_type == DEBUGGER_NATIVE) { debugger(context, address); } else { gdb_debug_enter(context, address); } +#endif } cd->m68k_pc = address; } diff -r b3832f73444f -r 6aca1734d573 sms.c --- a/sms.c Wed Mar 15 18:50:24 2023 -0700 +++ b/sms.c Wed Mar 15 19:28:11 2023 -0700 @@ -426,9 +426,11 @@ } if (sms->vdp->frame != sms->last_frame) { +#ifndef IS_LIB if (sms->psg->scope) { scope_render(sms->psg->scope); } +#endif uint32_t elapsed = sms->vdp->frame - sms->last_frame; sms->last_frame = sms->vdp->frame; if (system->enter_debugger_frames) { @@ -450,7 +452,9 @@ } if (system->enter_debugger && sms->z80->pc) { system->enter_debugger = 0; +#ifndef IS_LIB zdebugger(sms->z80, sms->z80->pc); +#endif } #ifdef NEW_CORE if (sms->z80->nmi_cycle == CYCLE_NEVER) { @@ -523,7 +527,9 @@ if (system->enter_debugger) { system->enter_debugger = 0; +#ifndef IS_LIB zinsert_breakpoint(sms->z80, sms->z80->pc, (uint8_t *)zdebugger); +#endif } run_sms(system); @@ -653,6 +659,7 @@ static void toggle_debug_view(system_header *system, uint8_t debug_view) { +#ifndef IS_LIB sms_context *sms = (sms_context *)system; if (debug_view < DEBUG_OSCILLOSCOPE) { vdp_toggle_debug_view(sms->vdp, debug_view); @@ -666,6 +673,7 @@ psg_enable_scope(sms->psg, scope, sms->normal_clock); } } +#endif } sms_context *alloc_configure_sms(system_media *media, uint32_t opts, uint8_t force_region) diff -r b3832f73444f -r 6aca1734d573 system.h --- a/system.h Wed Mar 15 18:50:24 2023 -0700 +++ b/system.h Wed Mar 15 19:28:11 2023 -0700 @@ -3,6 +3,7 @@ #include #include #include +#include "flac.h" typedef struct system_header system_header; typedef struct system_media system_media; @@ -111,6 +112,7 @@ typedef struct { FILE *f; + flac_file *flac; uint32_t file_offset; uint32_t fake_pregap; uint32_t pregap_lba; @@ -142,7 +144,7 @@ uint32_t cur_sector; media_type type; uint8_t in_fake_pregap; - uint8_t byte_storage; + uint8_t byte_storage[3]; }; #define OPT_ADDRESS_LOG (1U << 31U) diff -r b3832f73444f -r 6aca1734d573 ym2612.c --- a/ym2612.c Wed Mar 15 18:50:24 2023 -0700 +++ b/ym2612.c Wed Mar 15 19:28:11 2023 -0700 @@ -635,9 +635,11 @@ if (context->channels[i].logfile) { fwrite(&value, sizeof(value), 1, context->channels[i].logfile); } +#ifndef IS_LIB if (context->scope) { scope_add_sample(context->scope, context->channels[i].scope_channel, value, context->channels[i].phase_overflow); } +#endif if (context->channels[i].lr & 0x80) { left += (value * context->volume_mult) / context->volume_div; } else if (context->zero_offset) { @@ -1400,6 +1402,7 @@ void ym_enable_scope(ym2612_context *context, oscilloscope *scope, uint32_t master_clock) { +#ifndef IS_LIB static const char *names[] = { "YM2612 #1", "YM2612 #2", @@ -1413,4 +1416,5 @@ { context->channels[i].scope_channel = scope_add_channel(scope, names[i], master_clock / (context->clock_inc * NUM_OPERATORS)); } +#endif }