#include #define LIBSNES_IMPORT #include "snes/snes.hpp" #include "libsnes.hpp" #include #include #include #include #include #include HANDLE hPipe, hMapFile; void* hMapFilePtr; static bool running = false; enum eMessage : int { eMessage_Complete, eMessage_snes_library_id, eMessage_snes_library_revision_major, eMessage_snes_library_revision_minor, eMessage_snes_init, eMessage_snes_power, eMessage_snes_reset, eMessage_snes_run, eMessage_snes_term, eMessage_snes_unload_cartridge, //snes_set_cartridge_basename, //not used eMessage_snes_load_cartridge_normal, eMessage_snes_load_cartridge_super_game_boy, eMessage_snes_cb_video_refresh, eMessage_snes_cb_input_poll, eMessage_snes_cb_input_state, eMessage_snes_cb_input_notify, eMessage_snes_cb_audio_sample, eMessage_snes_cb_scanlineStart, eMessage_snes_cb_path_request, eMessage_snes_cb_trace_callback, eMessage_snes_get_region, eMessage_snes_get_memory_size, eMessage_snes_get_memory_data, eMessage_peek, eMessage_poke, eMessage_snes_serialize_size, eMessage_snes_serialize, eMessage_snes_unserialize, eMessage_snes_poll_message, eMessage_snes_dequeue_message, eMessage_snes_set_color_lut, eMessage_snes_enable_trace, eMessage_snes_enable_scanline, eMessage_snes_enable_audio, eMessage_snes_set_layer_enable, eMessage_snes_set_backdropColor, eMessage_snes_peek_logical_register, eMessage_snes_allocSharedMemory, eMessage_snes_freeSharedMemory, eMessage_GetMemoryIdName }; void ReadPipeBuffer(void* buf, int len) { DWORD bytesRead; BOOL result = ReadFile(hPipe, buf, len, &bytesRead, NULL); if(!result || bytesRead != len) exit(1); } template T ReadPipe() { T ret; ReadPipeBuffer(&ret,sizeof(ret)); return ret; } char* ReadPipeSharedPtr() { return (char*)hMapFilePtr + ReadPipe(); } template<> bool ReadPipe() { return !!ReadPipe(); } void WritePipeBuffer(const void* buf, int len) { //static FILE* outf = NULL; //if(!outf) outf = fopen("c:\\trace.bin","wb"); fwrite(buf,1,len,outf); fflush(outf); DWORD bytesWritten; BOOL result = WriteFile(hPipe, buf, len, &bytesWritten, NULL); if(!result || bytesWritten != len) exit(1); } template void WritePipe(const T& val) { WritePipeBuffer(&val, sizeof(val)); } void WritePipeString(const char* str) { int len = strlen(str); WritePipe(len); WritePipeBuffer(str,len); } std::string ReadPipeString() { int len = ReadPipe(); std::string ret; ret.resize(len); if(len!=0) ReadPipeBuffer(&ret[0],len); return ret; } typedef std::vector Blob; void WritePipeBlob(void* buf, int len) { WritePipe(len); WritePipeBuffer(buf,len); } Blob ReadPipeBlob() { int len = ReadPipe(); Blob ret; ret.resize(len); if(len!=0) ReadPipeBuffer(&ret[0],len); return ret; } void snes_video_refresh(const uint32_t *data, unsigned width, unsigned height) { WritePipe(eMessage_snes_cb_video_refresh); WritePipe(width); WritePipe(height); int destOfs = ReadPipe(); char* buf = (char*)hMapFilePtr + destOfs; int bufsize = 512 * 480 * 4; memcpy(buf,data,bufsize); WritePipe((char)0); //dummy synchronization } bool audio_en = false; static const int AUDIOBUFFER_SIZE = 44100*2; uint16_t audiobuffer[AUDIOBUFFER_SIZE]; int audiobuffer_idx = 0; void FlushAudio() { if(audiobuffer_idx == 0) return; WritePipe(eMessage_snes_cb_audio_sample); int nsamples = audiobuffer_idx; WritePipe(nsamples); char* buf = ReadPipeSharedPtr(); memcpy(buf,audiobuffer,nsamples*2); //extra just in case we had to unexpectedly flush audio and then carry on with some other process... yeah, its rickety. WritePipe(0); //dummy synchronization //wait for frontend to consume data ReadPipe(); //dummy synchronization WritePipe(0); //dummy synchronization audiobuffer_idx = 0; } void snes_audio_sample(uint16_t left, uint16_t right) { if(!audio_en) return; if(audiobuffer_idx == AUDIOBUFFER_SIZE) FlushAudio(); audiobuffer[audiobuffer_idx++] = left; audiobuffer[audiobuffer_idx++] = right; } void snes_input_poll(void) { WritePipe(eMessage_snes_cb_input_poll); } int16_t snes_input_state(unsigned port, unsigned device, unsigned index, unsigned id) { WritePipe(eMessage_snes_cb_input_state); WritePipe(port); WritePipe(device); WritePipe(index); WritePipe(id); return ReadPipe(); } void snes_input_notify(int index) { WritePipe(eMessage_snes_cb_input_notify); WritePipe(index); } void snes_trace(const char *msg) { WritePipe(eMessage_snes_cb_trace_callback); WritePipeString(msg); } const char* snes_path_request(int slot, const char* hint) { //yuck static char ret[MAX_PATH]; WritePipe(eMessage_snes_cb_path_request); WritePipe(slot); WritePipeString(hint); std::string str = ReadPipeString(); strcpy(ret,str.c_str()); return ret; } void RunMessageLoop(); void snes_scanlineStart(int line) { WritePipe(eMessage_snes_cb_scanlineStart); WritePipe(line); //we've got to wait for the frontend to finish processing. //in theory we could let emulation proceed after snagging the vram and registers, and do decoding and stuff on another thread... //but its too hard for now. RunMessageLoop(); } class SharedMemoryBlock { public: std::string memtype; HANDLE handle; }; static std::map memHandleTable; void* snes_allocSharedMemory(const char* memtype, size_t amt) { if(!running) return NULL; WritePipe(eMessage_snes_allocSharedMemory); WritePipeString(memtype); WritePipe(amt); std::string blockname = ReadPipeString(); auto mapfile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, blockname.c_str()); if(mapfile == INVALID_HANDLE_VALUE) return NULL; auto ptr = MapViewOfFile(mapfile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); auto smb = new SharedMemoryBlock(); smb->memtype = memtype; smb->handle = mapfile; memHandleTable[ptr] = smb; return ptr; } void snes_freeSharedMemory(void* ptr) { if(!running) return; if(!ptr) return; auto smb = memHandleTable.find(ptr)->second; UnmapViewOfFile(ptr); CloseHandle(smb->handle); WritePipe(eMessage_snes_freeSharedMemory); WritePipeString(smb->memtype.c_str()); } void InitBsnes() { //setup all hooks to forward messages to the frontend snes_set_video_refresh(snes_video_refresh); snes_set_audio_sample(snes_audio_sample); snes_set_input_poll(snes_input_poll); snes_set_input_state(snes_input_state); snes_set_input_notify(snes_input_notify); snes_set_path_request(snes_path_request); snes_set_allocSharedMemory(snes_allocSharedMemory); snes_set_freeSharedMemory(snes_freeSharedMemory); } void RunMessageLoop() { for(;;) { //printf("Reading message from pipe...\n"); auto msg = ReadPipe(); //printf("slam %08X\n",msg); switch(msg) { case eMessage_Complete: return; case eMessage_snes_library_id: WritePipeString(snes_library_id()); break; case eMessage_snes_library_revision_major: WritePipe(snes_library_revision_major()); break; case eMessage_snes_library_revision_minor: WritePipe(snes_library_revision_minor()); break; case eMessage_snes_init: snes_init(); WritePipe(eMessage_Complete); break; case eMessage_snes_power: snes_power(); break; case eMessage_snes_reset: snes_reset(); break; case eMessage_snes_run: FlushAudio(); snes_run(); FlushAudio(); WritePipe(eMessage_Complete); break; case eMessage_snes_term: snes_term(); break; case eMessage_snes_unload_cartridge: snes_unload_cartridge(); break; case eMessage_snes_load_cartridge_normal: { std::string xml = ReadPipeString(); Blob rom_data = ReadPipeBlob(); const char* xmlptr = NULL; if(xml != "") xmlptr = xml.c_str(); bool ret = snes_load_cartridge_normal(xmlptr,(unsigned char*)&rom_data[0],rom_data.size()); WritePipe(eMessage_Complete); WritePipe((char)(ret?1:0)); break; } case eMessage_snes_load_cartridge_super_game_boy: { std::string rom_xml = ReadPipeString(); const char* rom_xmlptr = NULL; if(rom_xml != "") rom_xmlptr = rom_xml.c_str(); Blob rom_data = ReadPipeBlob(); uint32 rom_length = rom_data.size(); std::string dmg_xml = ReadPipeString(); const char* dmg_xmlptr = NULL; if(dmg_xml != "") dmg_xmlptr = dmg_xml.c_str(); Blob dmg_data = ReadPipeBlob(); uint32 dmg_length = dmg_data.size(); bool ret = snes_load_cartridge_super_game_boy(rom_xmlptr,(uint8*)&rom_data[0],rom_length, dmg_xmlptr,(uint8*)&dmg_data[0], dmg_length); WritePipe(eMessage_Complete); WritePipe((char)(ret?1:0)); break; } case eMessage_snes_get_region: WritePipe((char)snes_get_region()); break; case eMessage_snes_get_memory_size: WritePipe(snes_get_memory_size(ReadPipe())); break; case eMessage_snes_get_memory_data: { unsigned int id = ReadPipe(); char* dstbuf = ReadPipeSharedPtr(); uint8_t* srcbuf = snes_get_memory_data(id); memcpy(dstbuf,srcbuf,snes_get_memory_size(id)); WritePipe(eMessage_Complete); break; } case eMessage_peek: { int id = ReadPipe(); unsigned int addr = ReadPipe(); uint8_t ret; if(id == SNES_MEMORY_SYSBUS) ret = bus_read(addr); else ret = snes_get_memory_data(id)[addr]; WritePipe(ret); } break; case eMessage_poke: { int id = ReadPipe(); unsigned int addr = ReadPipe(); uint8_t val = ReadPipe(); if(id == SNES_MEMORY_SYSBUS) bus_write(addr,val); else snes_get_memory_data(id)[addr] = val; break; } break; case eMessage_snes_serialize_size: WritePipe(snes_serialize_size()); break; case eMessage_snes_serialize: { int size = ReadPipe(); int destOfs = ReadPipe(); char* buf = (char*)hMapFilePtr + destOfs; bool ret = snes_serialize((uint8_t*)buf,size); WritePipe(eMessage_Complete); WritePipe((char)(ret?1:0)); break; } case eMessage_snes_unserialize: { //auto blob = ReadPipeBlob(); int size = ReadPipe(); int destOfs = ReadPipe(); char* buf = (char*)hMapFilePtr + destOfs; bool ret = snes_unserialize((uint8_t*)buf ,size); WritePipe(eMessage_Complete); WritePipe((char)(ret?1:0)); break; } case eMessage_snes_poll_message: //TBD WritePipe(-1); break; case eMessage_snes_dequeue_message: //TBD break; case eMessage_snes_set_color_lut: { auto blob = ReadPipeBlob(); snes_set_color_lut((uint32_t*)&blob[0]); break; } break; case eMessage_snes_enable_trace: if(!!ReadPipe()) snes_set_trace_callback(snes_trace); else snes_set_trace_callback(NULL); break; case eMessage_snes_enable_scanline: if(ReadPipe()) snes_set_scanlineStart(snes_scanlineStart); else snes_set_scanlineStart(NULL); break; case eMessage_snes_enable_audio: audio_en = ReadPipe(); break; case eMessage_snes_set_layer_enable: { int layer = ReadPipe(); int priority = ReadPipe(); bool enable = ReadPipe(); snes_set_layer_enable(layer,priority,enable); break; } case eMessage_snes_set_backdropColor: snes_set_backdropColor(ReadPipe()); break; case eMessage_snes_peek_logical_register: WritePipe(snes_peek_logical_register(ReadPipe())); break; case eMessage_GetMemoryIdName: { uint32 id = ReadPipe(); const char* ret = snes_get_memory_id_name(id); if(!ret) ret = ""; WritePipeString(ret); break; } } //switch(msg) } } void OpenConsole() { AllocConsole(); freopen("CONOUT$", "w", stdout); freopen("CONOUT$", "w", stderr); freopen("CONIN$", "r", stdin); } int xmain(int argc, char** argv) { if(argc != 2) { printf("This program is run from the libsneshawk emulator core. It is useless to you directly."); exit(1); } if(!strcmp(argv[1],"Bongizong")) { fprintf(stderr,"Honga Wongkong"); exit(0x16817); } char pipename[256]; sprintf(pipename, "\\\\.\\Pipe\\%s",argv[1]); if(!strncmp(argv[1],"console",7)) { OpenConsole(); } printf("pipe: %s\n",pipename); hPipe = CreateFile(pipename, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(hPipe == INVALID_HANDLE_VALUE) return 1; hMapFile = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE, argv[1]); if(hMapFile == INVALID_HANDLE_VALUE) return 1; hMapFilePtr = MapViewOfFile(hMapFile, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 0); running = true; printf("running\n"); RunMessageLoop(); return 0; } int CALLBACK WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) { int argc = __argc; char** argv = __argv; if(argc != 2) { if(IDOK == MessageBox(0,"This program is run from the libsneshawk emulator core. It is useless to you directly. But if you're really, that curious, click OK.","Whatfor my daddy-o",MB_OKCANCEL)) { ShellExecute(0,"open","http://www.youtube.com/watch?v=boanuwUMNNQ#t=98s",NULL,NULL,SW_SHOWNORMAL); } exit(1); } xmain(argc,argv); } void pwrap_init() { InitBsnes(); }