Cog/Plugins/HighlyComplete/HighlyComplete/HCDecoder.mm
Christopher Snowhill 387dcb3453 [Synthesizers] Implement default overrides
Default time, fade, loop count, and sample rate may now be overridden.

Synchronized preferences strings tables. Spanish translation of new
options pending, new releases won't be pushed until they're complete.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
2022-07-01 22:06:08 -07:00

1622 lines
40 KiB
Text

//
// 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 <psflib/psf2fs.h>
#import <psflib/psflib.h>
#import <HighlyExperimental/bios.h>
#import <HighlyExperimental/iop.h>
#import <HighlyExperimental/psx.h>
#import <HighlyExperimental/r3000.h>
#import <HighlyTheoretical/sega.h>
#import <HighlyQuixotic/qsound.h>
#import <mgba-util/vfs.h>
#import <mgba/core/blip_buf.h>
#import <mgba/core/core.h>
#import <mgba/core/log.h>
#import <SSEQPlayer/Player.h>
#import <SSEQPlayer/SDAT.h>
#include <vector>
#import <vio2sf/state.h>
#import <lazyusf2/usf.h>
#include <zlib.h>
#include <dlfcn.h>
#import "PlaylistController.h"
#include <signal.h>
// #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<CogSource> 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<CogSource> __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<CogSource> __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<CogSource> __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<uint8_t> sdatData;
std::unique_ptr<SDAT> sdat;
std::vector<uint8_t> 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<CogSource>)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;
switch(type) {
case 1:
case 2:
case 0x11:
case 0x12:
case 0x21:
case 0x22:
case 0x24:
case 0x25:
case 0x41:
break;
default:
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) {
double defaultLength = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthDefaultSeconds"] doubleValue];
double defaultFade = [[[[NSUserDefaultsController sharedUserDefaultsController] defaults] valueForKey:@"synthDefaultFadeSeconds"] doubleValue];
if(defaultLength < 0) {
defaultLength = 150.0;
}
if(defaultFade < 0) {
defaultFade = 0;
}
tagLengthMs = (int)ceil(defaultLength * 1000.0);
tagFadeMs = (int)ceil(defaultFade * 1000.0);
}
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<uint8_t> &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<uint8_t> &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