#include #include #include #include #include "platform/dipsw.h" #include "platform/vfs.h" #include "util/dprintf.h" #include "util/str.h" #include "util/crc.h" #define DATA_SIZE 503 #define BLOCK_SIZE (sizeof(uint32_t) + 4 + 1 + DATA_SIZE) #pragma pack(push, 1) typedef struct { uint32_t checksum; char padding[6]; uint8_t freeplay; char data[DATA_SIZE - 2]; } CreditBlock; typedef struct { uint32_t checksum; char padding[4]; uint8_t dip_switches; char data[DATA_SIZE]; } DipSwitchBlock; typedef struct { CreditBlock credit_block; DipSwitchBlock dip_switch_block; char *data; } SystemInfo; #pragma pack(pop) static SystemInfo system_info; static struct dipsw_config dipsw_config; static struct vfs_config vfs_config; static void dipsw_read_sysfile(const wchar_t *sys_file); static void dipsw_save_sysfile(const wchar_t *sys_file); HRESULT dipsw_init(const struct dipsw_config *cfg, const struct vfs_config *vfs_cfg) { HRESULT hr; wchar_t sys_file_path[MAX_PATH]; assert(cfg != NULL); assert(vfs_cfg != NULL); if (!cfg->enable) { return S_FALSE; } memcpy(&dipsw_config, cfg, sizeof(*cfg)); sys_file_path[0] = L'\0'; // concatenate vfs_config.amfs with L"sysfile.dat" wcsncpy(sys_file_path, vfs_cfg->amfs, MAX_PATH); wcsncat(sys_file_path, L"\\sysfile.dat", MAX_PATH); dipsw_read_sysfile(sys_file_path); // now write the dipsw_config.dipsw to the dip_switch_block dipsw_save_sysfile(sys_file_path); return S_OK; } static void dipsw_read_sysfile(const wchar_t *sys_file) { FILE *f = _wfopen(sys_file, L"r"); if (f == NULL) { dprintf("DipSw: First run detected, DipSw settings can only be applied AFTER the first run\n"); return; } fseek(f, 0, SEEK_END); long file_size = ftell(f); fseek(f, 0, SEEK_SET); if (file_size != 0x6000) { dprintf("DipSw: Invalid sysfile.dat file size\n"); fclose(f); return; } system_info.data = malloc(file_size); fread(system_info.data, 1, file_size, f); fclose(f); // copy the credit_block and dip_switch_block from the sysfile.dat memcpy(&system_info.credit_block, system_info.data, BLOCK_SIZE); memcpy(&system_info.dip_switch_block, system_info.data + 0x2800, BLOCK_SIZE); } static void dipsw_save_sysfile(const wchar_t *sys_file) { char block[BLOCK_SIZE]; uint8_t dipsw = 0; uint8_t freeplay = 0; // open the sysfile.dat for writing in bytes mode FILE *f = _wfopen(sys_file, L"rb+"); if (f == NULL) { return; } // write the dipsw_config.dipsw to the dip_switch_block for (int i = 0; i < 8; i++) { if (dipsw_config.dipsw[i]) { // print which dipsw is enabled dprintf("DipSw: DipSw%d=1 set\n", i + 1); dipsw |= (1 << i); } } if (dipsw_config.freeplay) { // print that freeplay is enabled dprintf("DipSw: Freeplay enabled\n"); freeplay = 1; } // set the new credit block system_info.credit_block.freeplay = freeplay; // set the new dip_switch_block system_info.dip_switch_block.dip_switches = dipsw; // calculate the new checksum, skip the old crc32 value // which is at the beginning of the block, thats's why the +4 // conver the struct to chars in order for the crc32 calculation to work system_info.credit_block.checksum = crc32( (char *)&system_info.credit_block + 4, BLOCK_SIZE - 4, 0); system_info.dip_switch_block.checksum = crc32( (char *)&system_info.dip_switch_block + 4, BLOCK_SIZE - 4, 0); // build the new credit block memcpy(block, (char *)&system_info.credit_block, BLOCK_SIZE); memcpy(system_info.data, block, BLOCK_SIZE); memcpy(system_info.data + 0x3000, block, BLOCK_SIZE); // build the new dip switch block memcpy(block, (char *)&system_info.dip_switch_block, BLOCK_SIZE); memcpy(system_info.data + 0x2800, block, BLOCK_SIZE); memcpy(system_info.data + 0x5800, block, BLOCK_SIZE); // print the dip_switch_block in hex /* dprintf("DipSw Block: "); for (size_t i = 0; i < BLOCK_SIZE; i++) { dprintf("%02X ", ((uint8_t *)&system_info.dip_switch_block)[i]); } dprintf("\n"); */ fwrite(system_info.data, 1, 0x6000, f); fclose(f); }