// // HCDecoder.m // HighlyComplete // // Created by Christopher Snowhill on 9/30/13. // Copyright 2013 __NoWork, Inc__. All rights reserved. // #import "HCDecoder.h" #import "Logging.h" #import "hebios.h" #import #import #import #import #import #import #import #import #import #import #import #import #import #import #include #import #import #include #include #import "PlaylistController.h" #include // #define USF_LOG @interface psf_file_container : NSObject { NSLock *lock; NSMutableDictionary *list; } + (psf_file_container *)instance; - (void)add_hint:(NSString *)path source:(id)source; - (void)remove_hint:(NSString *)path; - (BOOL)try_hint:(NSString *)path source:(id *)source; @end @implementation psf_file_container + (psf_file_container *)instance { static psf_file_container *instance; @synchronized(self) { if(!instance) { instance = [[self alloc] init]; } } return instance; } - (psf_file_container *)init { if((self = [super init])) { lock = [[NSLock alloc] init]; list = [[NSMutableDictionary alloc] initWithCapacity:0]; } return self; } - (void)add_hint:(NSString *)path source:(id)source { [lock lock]; [list setObject:source forKey:path]; [lock unlock]; } - (void)remove_hint:(NSString *)path { [lock lock]; [list removeObjectForKey:path]; [lock unlock]; } - (BOOL)try_hint:(NSString *)path source:(id *)source { [lock lock]; *source = [list objectForKey:path]; [lock unlock]; if(*source) { [*source seek:0 whence:0]; return YES; } else { return NO; } } @end void *source_fopen(const char *path) { id source; if(![[psf_file_container instance] try_hint:[NSString stringWithUTF8String:path] source:&source]) { NSString *urlString = [NSString stringWithUTF8String:path]; NSURL *url = [NSURL URLWithDataRepresentation:[urlString dataUsingEncoding:NSUTF8StringEncoding] relativeToURL:nil]; id audioSourceClass = NSClassFromString(@"AudioSource"); source = [audioSourceClass audioSourceForURL:url]; if(![source open:url]) return 0; if(![source seekable]) return 0; } return (void *)CFBridgingRetain(source); } size_t source_fread(void *buffer, size_t size, size_t count, void *handle) { NSObject *_handle = (__bridge NSObject *)(handle); id __unsafe_unretained source = (id)_handle; return [source read:buffer amount:(size * count)] / size; } int source_fseek(void *handle, int64_t offset, int whence) { NSObject *_handle = (__bridge NSObject *)(handle); id __unsafe_unretained source = (id)_handle; return [source seek:(long)offset whence:whence] ? 0 : -1; } int source_fclose(void *handle) { CFBridgingRelease(handle); return 0; } long source_ftell(void *handle) { NSObject *_handle = (__bridge NSObject *)(handle); id __unsafe_unretained source = (id)_handle; return [source tell]; } static psf_file_callbacks source_callbacks = { "/|\\", source_fopen, source_fread, source_fseek, source_fclose, source_ftell }; @implementation HCDecoder + (void)initialize { if(self == [HCDecoder class]) { bios_set_image(hebios, HEBIOS_SIZE); psx_init(); sega_init(); qsound_init(); mLogSetDefaultLogger(&gsf_logger); } } - (id)init { self = [super init]; if(self) { hintAdded = NO; type = 0; emulatorCore = NULL; emulatorExtra = NULL; } return self; } - (NSDictionary *)metadata { return metadataList; } - (long)retrieveFrameCount:(long)ms { return ms * (sampleRate / 100) / 10; } struct psf_info_meta_state { NSMutableDictionary *info; bool utf8; int tag_length_ms; int tag_fade_ms; float albumGain; float albumPeak; float trackGain; float trackPeak; float volume; }; static int parse_time_crap(NSString *value) { NSArray *crapFix = [value componentsSeparatedByString:@"\n"]; NSArray *components = [[crapFix objectAtIndex:0] componentsSeparatedByString:@":"]; float totalSeconds = 0; float multiplier = 1000; bool first = YES; for(id component in [components reverseObjectEnumerator]) { if(first) { first = NO; totalSeconds += [component floatValue] * multiplier; } else { totalSeconds += [component integerValue] * multiplier; } multiplier *= 60; } return totalSeconds; } static int psf_info_meta(void *context, const char *name, const char *value) { struct psf_info_meta_state *state = (struct psf_info_meta_state *)context; NSString *tag = guess_encoding_of_string(name); NSString *taglc = [tag lowercaseString]; NSString *svalue = guess_encoding_of_string(value); if(svalue == nil) return 0; if([taglc isEqualToString:@"game"]) { taglc = @"album"; } else if([taglc isEqualToString:@"date"]) { taglc = @"year"; } else if([taglc isEqualToString:@"album artist"]) { taglc = @"albumartist"; } else if([taglc isEqualToString:@"tracknumber"]) { taglc = @"track"; } else if([taglc isEqualToString:@"discnumber"]) { taglc = @"disc"; } if([taglc hasPrefix:@"replaygain_"]) { if([taglc hasPrefix:@"replaygain_album_"]) { if([taglc hasSuffix:@"gain"]) state->albumGain = [svalue floatValue]; else if([taglc hasSuffix:@"peak"]) state->albumPeak = [svalue floatValue]; } else if([taglc hasPrefix:@"replaygain_track_"]) { if([taglc hasSuffix:@"gain"]) state->trackGain = [svalue floatValue]; else if([taglc hasSuffix:@"peak"]) state->trackPeak = [svalue floatValue]; } } else if([taglc isEqualToString:@"volume"]) { state->volume = [svalue floatValue]; } else if([taglc isEqualToString:@"length"]) { state->tag_length_ms = parse_time_crap(svalue); } else if([taglc isEqualToString:@"fade"]) { state->tag_fade_ms = parse_time_crap(svalue); } else if([taglc isEqualToString:@"utf8"]) { state->utf8 = true; } else if([taglc isEqualToString:@"title"] || [taglc isEqualToString:@"albumartist"] || [taglc isEqualToString:@"artist"] || [taglc isEqualToString:@"album"] || [taglc isEqualToString:@"genre"]) { [state->info setObject:svalue forKey:taglc]; } else if([taglc isEqualToString:@"year"] || [taglc isEqualToString:@"track"] || [taglc isEqualToString:@"disc"]) { [state->info setObject:@([svalue intValue]) forKey:taglc]; } return 0; } struct psf1_load_state { void *emu; bool first; unsigned refresh; }; typedef struct { uint32_t pc0; uint32_t gp0; uint32_t t_addr; uint32_t t_size; uint32_t d_addr; uint32_t d_size; uint32_t b_addr; uint32_t b_size; uint32_t s_ptr; uint32_t s_size; uint32_t sp, fp, gp, ret, base; } exec_header_t; typedef struct { char key[8]; uint32_t text; uint32_t data; exec_header_t exec; char title[60]; } psxexe_hdr_t; static int psf1_info(void *context, const char *name, const char *value) { struct psf1_load_state *state = (struct psf1_load_state *)context; NSString *sname = [guess_encoding_of_string(name) lowercaseString]; NSString *svalue = guess_encoding_of_string(value); if(!state->refresh && [sname isEqualToString:@"_refresh"]) { state->refresh = [svalue intValue]; } return 0; } unsigned get_be16(void const *p) { return (unsigned)((unsigned char const *)p)[0] << 8 | (unsigned)((unsigned char const *)p)[1]; } unsigned get_le32(void const *p) { return (unsigned)((unsigned char const *)p)[3] << 24 | (unsigned)((unsigned char const *)p)[2] << 16 | (unsigned)((unsigned char const *)p)[1] << 8 | (unsigned)((unsigned char const *)p)[0]; } unsigned get_be32(void const *p) { return (unsigned)((unsigned char const *)p)[0] << 24 | (unsigned)((unsigned char const *)p)[1] << 16 | (unsigned)((unsigned char const *)p)[2] << 8 | (unsigned)((unsigned char const *)p)[3]; } void set_le32(void *p, unsigned n) { ((unsigned char *)p)[0] = (unsigned char)n; ((unsigned char *)p)[1] = (unsigned char)(n >> 8); ((unsigned char *)p)[2] = (unsigned char)(n >> 16); ((unsigned char *)p)[3] = (unsigned char)(n >> 24); } int psf1_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { struct psf1_load_state *state = (struct psf1_load_state *)context; psxexe_hdr_t *psx = (psxexe_hdr_t *)exe; if(exe_size < 0x800) return -1; if(exe_size > UINT_MAX) return -1; uint32_t addr = get_le32(&psx->exec.t_addr); uint32_t size = (uint32_t)exe_size - 0x800; addr &= 0x1fffff; if((addr < 0x10000) || (size > 0x1f0000) || (addr + size > 0x200000)) return -1; void *pIOP = psx_get_iop_state(state->emu); iop_upload_to_ram(pIOP, addr, exe + 0x800, size); if(!state->refresh) { if(!strncasecmp((const char *)exe + 113, "Japan", 5)) state->refresh = 60; else if(!strncasecmp((const char *)exe + 113, "Europe", 6)) state->refresh = 50; else if(!strncasecmp((const char *)exe + 113, "North America", 13)) state->refresh = 60; } if(state->first) { void *pR3000 = iop_get_r3000_state(pIOP); r3000_setreg(pR3000, R3000_REG_PC, get_le32(&psx->exec.pc0)); r3000_setreg(pR3000, R3000_REG_GEN + 29, get_le32(&psx->exec.s_ptr)); state->first = false; } return 0; } static int EMU_CALL virtual_readfile(void *context, const char *path, int offset, char *buffer, int length) { return psf2fs_virtual_readfile(context, path, offset, buffer, length); } struct sdsf_loader_state { uint8_t *data; size_t data_size; }; int sdsf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { if(exe_size < 4) return -1; struct sdsf_loader_state *state = (struct sdsf_loader_state *)context; uint8_t *dst = state->data; if(state->data_size < 4) { state->data = dst = (uint8_t *)malloc(exe_size); state->data_size = exe_size; memcpy(dst, exe, exe_size); return 0; } uint32_t dst_start = get_le32(dst); uint32_t src_start = get_le32(exe); dst_start &= 0x7fffff; src_start &= 0x7fffff; size_t dst_len = state->data_size - 4; size_t src_len = exe_size - 4; if(dst_len > 0x800000) dst_len = 0x800000; if(src_len > 0x800000) src_len = 0x800000; if(src_start < dst_start) { uint32_t diff = dst_start - src_start; state->data_size = dst_len + 4 + diff; state->data = dst = (uint8_t *)realloc(dst, state->data_size); memmove(dst + 4 + diff, dst + 4, dst_len); memset(dst + 4, 0, diff); dst_len += diff; dst_start = src_start; set_le32(dst, dst_start); } if((src_start + src_len) > (dst_start + dst_len)) { size_t diff = (src_start + src_len) - (dst_start + dst_len); state->data_size = dst_len + 4 + diff; state->data = dst = (uint8_t *)realloc(dst, state->data_size); memset(dst + 4 + dst_len, 0, diff); } memcpy(dst + 4 + (src_start - dst_start), exe + 4, src_len); return 0; } struct qsf_loader_state { uint8_t *key; uint32_t key_size; uint8_t *z80_rom; uint32_t z80_size; uint8_t *sample_rom; uint32_t sample_size; }; static int upload_qsf_section(struct qsf_loader_state *state, const char *section, uint32_t start, const uint8_t *data, uint32_t size) { uint8_t **array = NULL; uint32_t *array_size = NULL; uint32_t max_size = 0x7fffffff; if(!strcmp(section, "KEY")) { array = &state->key; array_size = &state->key_size; max_size = 11; } else if(!strcmp(section, "Z80")) { array = &state->z80_rom; array_size = &state->z80_size; } else if(!strcmp(section, "SMP")) { array = &state->sample_rom; array_size = &state->sample_size; } else return -1; if((start + size) < start) return -1; uint32_t new_size = start + size; uint32_t old_size = *array_size; if(new_size > max_size) return -1; if(new_size > old_size) { *array = (uint8_t *)realloc(*array, new_size); *array_size = new_size; memset((*array) + old_size, 0, new_size - old_size); } memcpy((*array) + start, data, size); return 0; } static int qsf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { struct qsf_loader_state *state = (struct qsf_loader_state *)context; for(;;) { char s[4]; if(exe_size < 11) break; memcpy(s, exe, 3); exe += 3; exe_size -= 3; s[3] = 0; uint32_t dataofs = get_le32(exe); exe += 4; exe_size -= 4; uint32_t datasize = get_le32(exe); exe += 4; exe_size -= 4; if(datasize > exe_size) return -1; if(upload_qsf_section(state, s, dataofs, exe, datasize) < 0) return -1; exe += datasize; exe_size -= datasize; } return 0; } struct gsf_loader_state { int entry_set; uint32_t entry; uint8_t *data; size_t data_size; }; static int gsf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { if(exe_size < 12) return -1; struct gsf_loader_state *state = (struct gsf_loader_state *)context; unsigned char *iptr; size_t isize; unsigned char *xptr; unsigned xentry = get_le32(exe + 0); unsigned xsize = get_le32(exe + 8); unsigned xofs = get_le32(exe + 4) & 0x1ffffff; if(xsize < exe_size - 12) return -1; if(!state->entry_set) { state->entry = xentry; state->entry_set = 1; } { iptr = state->data; isize = state->data_size; state->data = 0; state->data_size = 0; } if(!iptr) { size_t rsize = xofs + xsize; { rsize -= 1; rsize |= rsize >> 1; rsize |= rsize >> 2; rsize |= rsize >> 4; rsize |= rsize >> 8; rsize |= rsize >> 16; rsize += 1; } iptr = (unsigned char *)malloc(rsize + 10); if(!iptr) return -1; memset(iptr, 0, rsize + 10); isize = rsize; } else if(isize < xofs + xsize) { size_t rsize = xofs + xsize; { rsize -= 1; rsize |= rsize >> 1; rsize |= rsize >> 2; rsize |= rsize >> 4; rsize |= rsize >> 8; rsize |= rsize >> 16; rsize += 1; } xptr = (unsigned char *)realloc(iptr, xofs + rsize + 10); if(!xptr) { free(iptr); return -1; } iptr = xptr; isize = rsize; } memcpy(iptr + xofs, exe + 12, xsize); { state->data = iptr; state->data_size = isize; } return 0; } struct gsf_running_state { struct mAVStream stream; void *rom; int16_t samples[2048 * 2]; int buffered; }; static void _gsf_postAudioBuffer(struct mAVStream *stream, blip_t *left, blip_t *right) { struct gsf_running_state *state = (struct gsf_running_state *)stream; blip_read_samples(left, state->samples, 2048, true); blip_read_samples(right, state->samples + 1, 2048, true); state->buffered = 2048; } void GSFLogger(struct mLogger *logger, int category, enum mLogLevel level, const char *format, va_list args) { (void)logger; (void)category; (void)level; (void)format; (void)args; } static struct mLogger gsf_logger = { .log = GSFLogger, }; struct ncsf_loader_state { uint32_t sseq; std::vector sdatData; std::unique_ptr sdat; std::vector outputBuffer; ncsf_loader_state() : sseq(0) { } }; static int ncsf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { struct ncsf_loader_state *state = (struct ncsf_loader_state *)context; if(reserved_size >= 4) { state->sseq = get_le32(reserved); } if(exe_size >= 12) { uint32_t sdat_size = get_le32(exe + 8); if(sdat_size > exe_size) return -1; if(state->sdatData.empty()) state->sdatData.resize(sdat_size, 0); else if(state->sdatData.size() < sdat_size) state->sdatData.resize(sdat_size); memcpy(&state->sdatData[0], exe, sdat_size); } return 0; } struct twosf_loader_state { uint8_t *rom; uint8_t *state; size_t rom_size; size_t state_size; int initial_frames; int sync_type; int clockdown; int arm9_clockdown_level; int arm7_clockdown_level; }; static int load_twosf_map(struct twosf_loader_state *state, int issave, const unsigned char *udata, unsigned usize) { if(usize < 8) return -1; unsigned char *iptr; size_t isize; unsigned char *xptr; unsigned xsize = get_le32(udata + 4); unsigned xofs = get_le32(udata + 0); if(issave) { iptr = state->state; isize = state->state_size; state->state = 0; state->state_size = 0; } else { iptr = state->rom; isize = state->rom_size; state->rom = 0; state->rom_size = 0; } if(!iptr) { size_t rsize = xofs + xsize; if(!issave) { rsize -= 1; rsize |= rsize >> 1; rsize |= rsize >> 2; rsize |= rsize >> 4; rsize |= rsize >> 8; rsize |= rsize >> 16; rsize += 1; } iptr = (unsigned char *)malloc(rsize + 10); if(!iptr) return -1; memset(iptr, 0, rsize + 10); isize = rsize; } else if(isize < xofs + xsize) { size_t rsize = xofs + xsize; if(!issave) { rsize -= 1; rsize |= rsize >> 1; rsize |= rsize >> 2; rsize |= rsize >> 4; rsize |= rsize >> 8; rsize |= rsize >> 16; rsize += 1; } xptr = (unsigned char *)realloc(iptr, xofs + rsize + 10); if(!xptr) { free(iptr); return -1; } iptr = xptr; isize = rsize; } memcpy(iptr + xofs, udata + 8, xsize); if(issave) { state->state = iptr; state->state_size = isize; } else { state->rom = iptr; state->rom_size = isize; } return 0; } static int load_twosf_mapz(struct twosf_loader_state *state, int issave, const unsigned char *zdata, unsigned zsize, unsigned zcrc) { int ret; int zerr; uLongf usize = 8; uLongf rsize = usize; unsigned char *udata; unsigned char *rdata; udata = (unsigned char *)malloc(usize); if(!udata) return -1; while(Z_OK != (zerr = uncompress(udata, &usize, zdata, zsize))) { if(Z_MEM_ERROR != zerr && Z_BUF_ERROR != zerr) { free(udata); return -1; } if(usize >= 8) { usize = get_le32(udata + 4) + 8; if(usize < rsize) { rsize += rsize; usize = rsize; } else rsize = usize; } else { rsize += rsize; usize = rsize; } rdata = (unsigned char *)realloc(udata, usize); if(!rdata) { free(udata); return -1; } udata = rdata; } rdata = (unsigned char *)realloc(udata, usize); if(!rdata) { free(udata); return -1; } { uLong ccrc = crc32(crc32(0L, Z_NULL, 0), rdata, (uInt)usize); if(ccrc != zcrc) return -1; } ret = load_twosf_map(state, issave, rdata, (unsigned)usize); free(rdata); return ret; } static int twosf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { struct twosf_loader_state *state = (struct twosf_loader_state *)context; if(exe_size >= 8) { if(load_twosf_map(state, 0, exe, (unsigned)exe_size)) return -1; } if(reserved_size) { size_t resv_pos = 0; if(reserved_size < 16) return -1; while(resv_pos + 12 < reserved_size) { unsigned save_size = get_le32(reserved + resv_pos + 4); unsigned save_crc = get_le32(reserved + resv_pos + 8); if(get_le32(reserved + resv_pos + 0) == 0x45564153) { if(resv_pos + 12 + save_size > reserved_size) return -1; if(load_twosf_mapz(state, 1, reserved + resv_pos + 12, save_size, save_crc)) return -1; } resv_pos += 12 + save_size; } } return 0; } static int twosf_info(void *context, const char *name, const char *value) { struct twosf_loader_state *state = (struct twosf_loader_state *)context; NSString *sname = [guess_encoding_of_string(name) lowercaseString]; NSString *svalue = guess_encoding_of_string(value); if([sname isEqualToString:@"_frames"]) { state->initial_frames = [svalue intValue]; } else if([sname isEqualToString:@"_clockdown"]) { state->clockdown = [svalue intValue]; } else if([sname isEqualToString:@"_vio2sf_sync_type"]) { state->sync_type = [svalue intValue]; } else if([sname isEqualToString:@"_vio2sf_arm9_clockdown_level"]) { state->arm9_clockdown_level = [svalue intValue]; } else if([sname isEqualToString:@"_vio2sf_arm7_clockdown_level"]) { state->arm7_clockdown_level = [svalue intValue]; } return 0; } struct usf_loader_state { uint32_t enablecompare; uint32_t enablefifofull; void *emu_state; }; static int usf_loader(void *context, const uint8_t *exe, size_t exe_size, const uint8_t *reserved, size_t reserved_size) { struct usf_loader_state *uUsf = (struct usf_loader_state *)context; if(exe && exe_size > 0) return -1; return usf_upload_section(uUsf->emu_state, reserved, reserved_size); } static int usf_info(void *context, const char *name, const char *value) { struct usf_loader_state *uUsf = (struct usf_loader_state *)context; NSString *sname = [guess_encoding_of_string(name) lowercaseString]; NSString *svalue = guess_encoding_of_string(value); if([sname isEqualToString:@"_enablecompare"] && [svalue length]) uUsf->enablecompare = 1; else if([sname isEqualToString:@"_enablefifofull"] && [svalue length]) uUsf->enablefifofull = 1; return 0; } - (BOOL)initializeDecoder { unsigned int silence_seconds = 5; usfRemoveSilence = NO; if(type == 1) { emulatorCore = (uint8_t *)malloc(psx_get_state_size(1)); psx_clear_state(emulatorCore, 1); struct psf1_load_state state; state.emu = emulatorCore; state.first = true; state.refresh = 0; if(psf_load([currentUrl UTF8String], &source_callbacks, 1, psf1_loader, &state, psf1_info, &state, 1) <= 0) return NO; if(state.refresh) psx_set_refresh(emulatorCore, state.refresh); silence_seconds = 30; } else if(type == 2) { emulatorExtra = psf2fs_create(); struct psf1_load_state state; state.refresh = 0; if(psf_load([currentUrl UTF8String], &source_callbacks, 2, psf2fs_load_callback, emulatorExtra, psf1_info, &state, 1) <= 0) return NO; emulatorCore = (uint8_t *)malloc(psx_get_state_size(2)); psx_clear_state(emulatorCore, 2); if(state.refresh) psx_set_refresh(emulatorCore, state.refresh); psx_set_readfile(emulatorCore, virtual_readfile, emulatorExtra); silence_seconds = 30; } else if(type == 0x11 || type == 0x12) { struct sdsf_loader_state state; memset(&state, 0, sizeof(state)); if(psf_load([currentUrl UTF8String], &source_callbacks, type, sdsf_loader, &state, 0, 0, 0) <= 0) return NO; emulatorCore = (uint8_t *)malloc(sega_get_state_size(type - 0x10)); sega_clear_state(emulatorCore, type - 0x10); sega_enable_dry(emulatorCore, 1); sega_enable_dsp(emulatorCore, 1); sega_enable_dsp_dynarec(emulatorCore, 0); uint32_t start = *(uint32_t *)state.data; size_t length = state.data_size; const size_t max_length = (type == 0x12) ? 0x800000 : 0x80000; if((start + (length - 4)) > max_length) { length = max_length - start + 4; } sega_upload_program(emulatorCore, state.data, (uint32_t)length); free(state.data); } else if(type == 0x21) { struct usf_loader_state state; memset(&state, 0, sizeof(state)); state.emu_state = malloc(usf_get_state_size()); if(!state.emu_state) return NO; usf_clear(state.emu_state); usf_set_hle_audio(state.emu_state, 1); emulatorCore = (uint8_t *)state.emu_state; if(psf_load([currentUrl UTF8String], &source_callbacks, 0x21, usf_loader, &state, usf_info, &state, 1) <= 0) return NO; usf_set_compare(state.emu_state, state.enablecompare); usf_set_fifo_full(state.emu_state, state.enablefifofull); usfRemoveSilence = YES; silence_seconds = 10; } else if(type == 0x22) { struct gsf_loader_state state; memset(&state, 0, sizeof(state)); if(psf_load([currentUrl UTF8String], &source_callbacks, 0x22, gsf_loader, &state, 0, 0, 0) <= 0) return NO; if(state.data_size > UINT_MAX) return NO; /*FILE * f = fopen("/tmp/rom.gba", "wb"); fwrite(state.data, 1, state.data_size, f); fclose(f);*/ struct VFile *rom = VFileFromConstMemory(state.data, state.data_size); if(!rom) { free(state.data); return NO; } struct mCore *core = mCoreFindVF(rom); if(!core) { free(state.data); return NO; } struct gsf_running_state *rstate = (struct gsf_running_state *)calloc(1, sizeof(*rstate)); if(!rstate) { core->deinit(core); free(state.data); return NO; } rstate->rom = state.data; rstate->stream.postAudioBuffer = _gsf_postAudioBuffer; core->init(core); core->setAVStream(core, &rstate->stream); mCoreInitConfig(core, NULL); core->setAudioBufferSize(core, 2048); blip_set_rates(core->getAudioChannel(core, 0), core->frequency(core), 44100); blip_set_rates(core->getAudioChannel(core, 1), core->frequency(core), 44100); struct mCoreOptions opts = { .useBios = false, .skipBios = true, .volume = 0x100, .sampleRate = 44100, }; mCoreConfigLoadDefaults(&core->config, &opts); core->loadROM(core, rom); core->reset(core); emulatorCore = (uint8_t *)core; emulatorExtra = rstate; } else if(type == 0x24) { struct twosf_loader_state state; memset(&state, 0, sizeof(state)); state.initial_frames = -1; if(psf_load([currentUrl UTF8String], &source_callbacks, 0x24, twosf_loader, &state, twosf_info, &state, 1) <= 0) { if(state.rom) free(state.rom); if(state.state) free(state.state); return NO; } if(state.rom_size > UINT_MAX || state.state_size > UINT_MAX) { if(state.rom) free(state.rom); if(state.state) free(state.state); return NO; } NDS_state *core = (NDS_state *)calloc(1, sizeof(NDS_state)); if(!core) { if(state.rom) free(state.rom); if(state.state) free(state.state); return NO; } if(state_init(core)) { state_deinit(core); if(state.rom) free(state.rom); if(state.state) free(state.state); return NO; } int resampling_int = -1; NSString *resampling = [[NSUserDefaults standardUserDefaults] stringForKey:@"resampling"]; if([resampling isEqualToString:@"zoh"]) resampling_int = 0; else if([resampling isEqualToString:@"blep"]) resampling_int = 1; else if([resampling isEqualToString:@"linear"]) resampling_int = 2; else if([resampling isEqualToString:@"blam"]) resampling_int = 3; else if([resampling isEqualToString:@"cubic"]) resampling_int = 4; else if([resampling isEqualToString:@"sinc"]) resampling_int = 5; core->dwInterpolation = resampling_int; core->dwChannelMute = 0; if(!state.arm7_clockdown_level) state.arm7_clockdown_level = state.clockdown; if(!state.arm9_clockdown_level) state.arm9_clockdown_level = state.clockdown; core->initial_frames = state.initial_frames; core->sync_type = state.sync_type; core->arm7_clockdown_level = state.arm7_clockdown_level; core->arm9_clockdown_level = state.arm9_clockdown_level; emulatorCore = (uint8_t *)core; emulatorExtra = state.rom; if(state.rom) state_setrom(core, state.rom, (u32)state.rom_size, 0); state_loadstate(core, state.state, (u32)state.state_size); if(state.state) free(state.state); } else if(type == 0x25) { struct ncsf_loader_state *state = new struct ncsf_loader_state; if(psf_load([currentUrl UTF8String], &source_callbacks, 0x25, ncsf_loader, state, 0, 0, 0) <= 0) { delete state; return NO; } Player *player = new Player; player->interpolation = INTERPOLATION_SINC; PseudoFile file; file.data = &state->sdatData; state->sdat.reset(new SDAT(file, state->sseq)); auto *sseqToPlay = state->sdat->sseq.get(); player->sampleRate = 44100; player->Setup(sseqToPlay); player->Timer(); state->outputBuffer.resize(1024 * sizeof(int16_t) * 2); emulatorCore = (uint8_t *)player; emulatorExtra = state; } else if(type == 0x41) { struct qsf_loader_state *state = (struct qsf_loader_state *)calloc(1, sizeof(*state)); emulatorExtra = state; if(psf_load([currentUrl UTF8String], &source_callbacks, 0x41, qsf_loader, state, 0, 0, 0) <= 0) return NO; emulatorCore = (uint8_t *)malloc(qsound_get_state_size()); qsound_clear_state(emulatorCore); if(state->key_size == 11) { uint8_t *ptr = state->key; uint32_t swap_key1 = get_be32(ptr + 0); uint32_t swap_key2 = get_be32(ptr + 4); uint32_t addr_key = get_be16(ptr + 8); uint8_t xor_key = *(ptr + 10); qsound_set_kabuki_key(emulatorCore, swap_key1, swap_key2, addr_key, xor_key); } else { qsound_set_kabuki_key(emulatorCore, 0, 0, 0, 0); } qsound_set_z80_rom(emulatorCore, state->z80_rom, state->z80_size); qsound_set_sample_rom(emulatorCore, state->sample_rom, state->sample_size); } else return NO; if(type == 1 || type == 2) { void *pIOP = psx_get_iop_state(emulatorCore); iop_set_compat(pIOP, IOP_COMPAT_HARSH); } framesRead = 0; silence_test_buffer.resize(sampleRate * silence_seconds * 2); if(![self fillBuffer]) return NO; silence_test_buffer.remove_leading_silence(); return YES; } - (BOOL)open:(id)source { if(![source seekable]) { return NO; } currentSource = source; struct psf_info_meta_state info; info.info = [NSMutableDictionary dictionary]; info.utf8 = false; info.tag_length_ms = 0; info.tag_fade_ms = 0; info.albumGain = 0; info.albumPeak = 0; info.trackGain = 0; info.trackPeak = 0; info.volume = 1; currentUrl = [[[source url] absoluteString] stringByRemovingPercentEncoding]; [[psf_file_container instance] add_hint:currentUrl source:currentSource]; hintAdded = YES; type = psf_load([currentUrl UTF8String], &source_callbacks, 0, 0, 0, psf_info_meta, &info, 0); if(type <= 0) return NO; emulatorCore = nil; emulatorExtra = nil; sampleRate = 44100; if(type == 2) sampleRate = 48000; else if(type == 0x41) sampleRate = 24038; tagLengthMs = info.tag_length_ms; tagFadeMs = info.tag_fade_ms; if(!tagLengthMs) { tagLengthMs = (2 * 60 + 30) * 1000; tagFadeMs = 8000; } replayGainAlbumGain = info.albumGain; replayGainAlbumPeak = info.albumPeak; replayGainTrackGain = info.trackGain; replayGainTrackPeak = info.trackPeak; volume = info.volume; metadataList = info.info; framesLength = [self retrieveFrameCount:tagLengthMs]; totalFrames = [self retrieveFrameCount:tagLengthMs + tagFadeMs]; [self willChangeValueForKey:@"properties"]; [self didChangeValueForKey:@"properties"]; return YES; } - (BOOL)fillBuffer { long frames_left = totalFrames - framesRead - silence_test_buffer.data_available() / 2; long free_space = silence_test_buffer.free_space() / 2; if(IsRepeatOneSet()) frames_left = free_space; if(free_space > frames_left) free_space = frames_left; while(free_space > 0) { unsigned long samples_to_write = 0; int16_t *buf = silence_test_buffer.get_write_ptr(samples_to_write); int samples_read = [self readAudioInternal:buf frames:(UInt32)samples_to_write / 2]; if(!samples_read) break; silence_test_buffer.samples_written(samples_read * 2); free_space -= samples_read; } return !silence_test_buffer.test_silence(); } - (int)readAudioInternal:(void *)buf frames:(UInt32)frames { if(type == 1 || type == 2) { uint32_t howmany = frames; psx_execute(emulatorCore, 0x7fffffff, (int16_t *)buf, &howmany, 0); frames = howmany; } else if(type == 0x11 || type == 0x12) { uint32_t howmany = frames; sega_execute(emulatorCore, 0x7fffffff, (int16_t *)buf, &howmany); frames = howmany; } else if(type == 0x21) { const char *err; if((err = usf_render_resampled(emulatorCore, (int16_t *)buf, frames, sampleRate)) != 0) { DLog(@"USF Error: %s", err); return 0; } } else if(type == 0x22) { struct mCore *core = (struct mCore *)emulatorCore; struct gsf_running_state *rstate = (struct gsf_running_state *)emulatorExtra; unsigned long frames_to_render = frames; do { unsigned long frames_rendered = rstate->buffered; if(frames_rendered >= frames_to_render) { memcpy(buf, rstate->samples, frames_to_render * 4); frames_rendered -= frames_to_render; memcpy(rstate->samples, rstate->samples + frames_to_render * 2, frames_rendered * 4); frames_to_render = 0; } else { memcpy(buf, rstate->samples, frames_rendered * 4); buf = ((uint8_t *)buf) + frames_rendered * 4; frames_to_render -= frames_rendered; frames_rendered = 0; } rstate->buffered = (int)frames_rendered; if(frames_to_render) { while(!rstate->buffered) core->runFrame(core); } } while(frames_to_render); } else if(type == 0x24) { NDS_state *state = (NDS_state *)emulatorCore; state_render(state, (s16 *)buf, frames); } else if(type == 0x25) { Player *player = (Player *)emulatorCore; ncsf_loader_state *state = (ncsf_loader_state *)emulatorExtra; std::vector &buffer = state->outputBuffer; unsigned long frames_to_do = frames; while(frames_to_do) { unsigned frames_this_run = 1024; if(frames_this_run > frames_to_do) frames_this_run = (unsigned int)frames_to_do; player->GenerateSamples(buffer, 0, frames_this_run); memcpy(buf, &buffer[0], frames_this_run * sizeof(int16_t) * 2); buf = ((uint8_t *)buf) + frames_this_run * sizeof(int16_t) * 2; frames_to_do -= frames_this_run; } } else if(type == 0x41) { uint32_t howmany = frames; qsound_execute(emulatorCore, 0x7fffffff, (int16_t *)buf, &howmany); frames = howmany; } return frames; } - (int)readAudio:(void *)buf frames:(UInt32)frames { if(!emulatorCore) { if(![self initializeDecoder]) return 0; } else if(![self fillBuffer]) return 0; if(usfRemoveSilence) { silence_test_buffer.remove_leading_silence(); usfRemoveSilence = NO; } unsigned long written = silence_test_buffer.data_available() / 2; if(written > frames) written = frames; if(!IsRepeatOneSet() && written > totalFrames - framesRead) written = totalFrames - framesRead; if(written == 0) return 0; silence_test_buffer.read((int16_t *)buf, written * 2); if(!IsRepeatOneSet() && framesRead + written > framesLength) { long fadeStart = (framesLength > framesRead) ? framesLength : framesRead; long fadeEnd = framesRead + written; long fadeTotal = totalFrames - framesLength; long fadePos; int16_t *buf16 = (int16_t *)buf; for(fadePos = fadeStart; fadePos < fadeEnd; ++fadePos) { long scale = totalFrames - fadePos; buf16[0] = buf16[0] * scale / fadeTotal; buf16[1] = buf16[1] * scale / fadeTotal; buf16 += 2; } } framesRead += written; return (int)written; } - (void)closeDecoder { if(emulatorCore) { if(type == 0x21) { usf_shutdown(emulatorCore); free(emulatorCore); } else if(type == 0x22) { struct mCore *core = (struct mCore *)emulatorCore; core->deinit(core); } else if(type == 0x24) { NDS_state *state = (NDS_state *)emulatorCore; state_deinit(state); free(state); } else if(type == 0x25) { Player *player = (Player *)emulatorCore; delete player; } else { free(emulatorCore); } emulatorCore = nil; } if(type == 2 && emulatorExtra) { psf2fs_delete(emulatorExtra); emulatorExtra = nil; } else if(type == 0x22 && emulatorExtra) { struct gsf_running_state *rstate = (struct gsf_running_state *)emulatorExtra; free(rstate->rom); free(rstate); emulatorExtra = nil; } else if(type == 0x24 && emulatorExtra) { free(emulatorExtra); emulatorExtra = nil; } else if(type == 0x25 && emulatorExtra) { struct ncsf_loader_state *state = (struct ncsf_loader_state *)emulatorExtra; delete state; emulatorExtra = nil; } else if(type == 0x41 && emulatorExtra) { struct qsf_loader_state *state = (struct qsf_loader_state *)emulatorExtra; free(state->key); free(state->z80_rom); free(state->sample_rom); free(state); emulatorExtra = nil; } } - (void)close { [self closeDecoder]; currentSource = nil; if(hintAdded) { [[psf_file_container instance] remove_hint:currentUrl]; hintAdded = NO; } currentUrl = nil; } - (void)dealloc { [self close]; } - (long)seek:(long)frame { if(frame < framesRead || emulatorCore == NULL) { [self closeDecoder]; if(![self initializeDecoder]) return -1; if(usfRemoveSilence) { silence_test_buffer.remove_leading_silence(); usfRemoveSilence = NO; } } unsigned long buffered_samples = silence_test_buffer.data_available() / 2; if(buffered_samples >= (frame - framesRead)) { frame -= framesRead; silence_test_buffer.read(NULL, frame * 2); framesRead += frame; return framesRead; } else if(buffered_samples) { silence_test_buffer.read(NULL, buffered_samples * 2); framesRead += buffered_samples; } if(type == 1 || type == 2) { do { uint32_t howmany = (uint32_t)(frame - framesRead); if(psx_execute(emulatorCore, 0x7fffffff, 0, &howmany, 0) < 0) break; framesRead += howmany; } while(framesRead < frame); } else if(type == 0x11 || type == 0x12) { do { uint32_t howmany = (uint32_t)(frame - framesRead); if(sega_execute(emulatorCore, 0x7fffffff, 0, &howmany) < 0) break; framesRead += howmany; } while(framesRead < frame); } else if(type == 0x21) { do { ssize_t howmany = frame - framesRead; if(howmany > 1024) howmany = 1024; if(usf_render_resampled(emulatorCore, NULL, howmany, sampleRate) != 0) return -1; framesRead += howmany; } while(framesRead < frame); } else if(type == 0x22) { struct mCore *core = (struct mCore *)emulatorCore; struct gsf_running_state *rstate = (struct gsf_running_state *)emulatorExtra; long frames_to_run = frame - framesRead; do { if(frames_to_run >= rstate->buffered) { frames_to_run -= rstate->buffered; rstate->buffered = 0; } else { rstate->buffered -= frames_to_run; memcpy(rstate->samples, rstate->samples + frames_to_run * 4, rstate->buffered * 4); frames_to_run = 0; } if(frames_to_run) { while(!rstate->buffered) core->runFrame(core); } } while(frames_to_run); framesRead = frame; } else if(type == 0x24) { NDS_state *state = (NDS_state *)emulatorCore; s16 temp[2048]; long frames_to_run = frame - framesRead; while(frames_to_run) { unsigned frames_this_run = 1024; if(frames_this_run > frames_to_run) frames_this_run = (unsigned)frames_to_run; state_render(state, temp, frames_this_run); frames_to_run -= frames_this_run; } framesRead = frame; } else if(type == 0x25) { Player *player = (Player *)emulatorCore; ncsf_loader_state *state = (ncsf_loader_state *)emulatorExtra; std::vector &buffer = state->outputBuffer; long frames_to_run = frame - framesRead; while(frames_to_run) { int frames_to_render = 1024; if(frames_to_render > frames_to_run) frames_to_render = (int)frames_to_run; player->GenerateSamples(buffer, 0, frames_to_render); frames_to_run -= frames_to_render; } framesRead = frame; } else if(type == 0x41) { do { uint32_t howmany = (uint32_t)(frame - framesRead); if(qsound_execute(emulatorCore, 0x7fffffff, 0, &howmany) < 0) break; framesRead += howmany; } while(framesRead < frame); } return framesRead; } - (NSDictionary *)properties { NSString *codec = @""; switch(type) { case 1: codec = @"PSF"; break; case 2: codec = @"PSF2"; break; case 0x11: codec = @"SSF"; break; case 0x12: codec = @"DSF"; break; case 0x21: codec = @"USF"; break; case 0x22: codec = @"GSF"; break; case 0x24: codec = @"2SF"; break; case 0x25: codec = @"NCSF"; break; case 0x41: codec = @"QSF"; break; } return @{ @"channels": @(2), @"bitsPerSample": @(16), @"sampleRate": @(sampleRate), @"totalFrames": @(totalFrames), @"bitrate": @(0), @"seekable": @(YES), @"replayGainAlbumGain": @(replayGainAlbumGain), @"replayGainAlbumPeak": @(replayGainAlbumPeak), @"replayGainTrackGain": @(replayGainTrackGain), @"replayGainTrackPeak": @(replayGainTrackPeak), @"volume": @(volume), @"codec": codec, @"endian": @"host", @"encoding": @"synthesized" }; } + (NSDictionary *)metadataForURL:(NSURL *)url { struct psf_info_meta_state info; info.info = [NSMutableDictionary dictionary]; info.utf8 = false; info.tag_length_ms = 0; info.tag_fade_ms = 0; NSString *decodedUrl = [[url absoluteString] stringByRemovingPercentEncoding]; psf_load([decodedUrl UTF8String], &source_callbacks, 0, 0, 0, psf_info_meta, &info, 0); return [NSDictionary dictionaryWithDictionary:info.info]; } + (NSArray *)fileTypes { return @[@"psf", @"minipsf", @"psf2", @"minipsf2", @"ssf", @"minissf", @"dsf", @"minidsf", @"qsf", @"miniqsf", @"gsf", @"minigsf", @"ncsf", @"minincsf", @"2sf", @"mini2sf", @"usf", @"miniusf"]; } + (NSArray *)mimeTypes { return @[@"audio/x-psf"]; } + (float)priority { return 1.0; } + (NSArray *)fileTypeAssociations { NSMutableArray *ret = [[NSMutableArray alloc] init]; [ret addObject:@"PSF Format Files"]; [ret addObject:@"vg.icns"]; [ret addObjectsFromArray:[self fileTypes]]; return @[[NSArray arrayWithArray:ret]]; } @end