#include "driver2.h" #ifndef PSX #include #include #endif // !PSX #include "system.h" #include "platform.h" #include "xaplay.h" #include "loadview.h" #include "mission.h" #include "glaunch.h" #include "main.h" #include "pad.h" #include "draw.h" #include "pause.h" char gDataFolder[32] = "DRIVER2\\"; #ifdef PSX /* those should be passed to linker script ------------------------------------------------------------------------------------ NAME | OFFSET | SIZE | DESCRIPTION ------------------------|----------|---------|-------------------------------------- _path_org | 0xE7000 | 0xB7C8 | _otag1_org | 0xF3000 | 0x4200 | _otag2_org | 0xF7200 | 0x4200 | _primTab1_org | 0xFB400 | 1E000 | _primTab2_org | 0x119400 | 1E000 | _mallocTab_org | 0x137400 | 0xD47BC |(this is bigger, real is 0xC37BC) _frnt_org | 0x1C0000 | no size | inside mallocTab _sbnk_org | 0x180000 | no size | inside mallocTab _repl_org | 0x1FABBC | 0x3444 | probably inside mallocTab */ #else // Initialized in redriver2_main volatile char* _frontend_buffer = NULL; // 0xFB400 volatile char* _other_buffer = NULL; // 0xF3000 volatile char* _other_buffer2 = NULL; // 0xE7000 volatile OTTYPE* _OT1 = NULL; // 0xF3000 volatile OTTYPE* _OT2 = NULL; // 0xF7200 volatile char* _primTab1 = NULL; // 0xFB400 volatile char* _primTab2 = NULL; // 0x119400 volatile char* _sbank_buffer = NULL; // 0x180000 volatile char* _overlay_buffer = NULL; // 0x1C0000 volatile char* _replay_buffer = NULL; // 0x1FABBC #endif #if defined(USE_CRT_MALLOC) char* mallocptr = NULL; volatile char* malloctab = NULL; void* g_dynamicAllocs[1024] = { 0 }; int g_numDynamicAllocs = 0; char* sys_malloc(int size, char* funcname, int line) { if (g_numDynamicAllocs & 0x10000) sys_tempfree(); #ifdef _DEBUG printWarning("CRT malloc(%d) in %s, line %d\n", size, funcname, __LINE__); #endif // _DEBUG void* ptr = malloc(size); g_dynamicAllocs[g_numDynamicAllocs++] = ptr; return (char*)ptr; } void sys_freeall() { int numAlloc = g_numDynamicAllocs & 0xfff; for (int i = 0; i < numAlloc; i++) { free(g_dynamicAllocs[i]); g_dynamicAllocs[i] = NULL; } g_numDynamicAllocs = 0; } char* sys_tempalloc(int size) { if (g_numDynamicAllocs & 0x10000) { printError("sys_tempalloc: another alloc in use!\n"); trap(1000); return NULL; } char* tmp_ptr = (char*)malloc(size); g_dynamicAllocs[g_numDynamicAllocs] = tmp_ptr; g_numDynamicAllocs |= 0x10000; return tmp_ptr; } void sys_tempfree() { g_numDynamicAllocs &= ~0x10000; free(g_dynamicAllocs[g_numDynamicAllocs]); g_dynamicAllocs[g_numDynamicAllocs] = NULL; } #elif !defined(PSX) char g_allocatedMem[0x200000]; // 0x137400 (_ramsize). TODO: use real malloc size: 870332 volatile char* mallocptr = g_allocatedMem; volatile char* malloctab = g_allocatedMem; #endif int leadAIRequired = 0; int leadAILoaded = 0; int pathAILoaded = 0; char* LevelNames[] = { "CHICAGO", "HAVANA", "VEGAS", "RIO", }; char* LevelFiles[] = { "LEVELS\\CHICAGO.LEV", "LEVELS\\HAVANA.LEV", "LEVELS\\VEGAS.LEV", "LEVELS\\RIO.LEV", }; char* LoadingScreenNames[] = { "GFX\\LOADCHIC.TIM", "GFX\\LOADHAVA.TIM", "GFX\\LOADVEGA.TIM", "GFX\\LOADRIO.TIM", }; CdlFILE currentfileinfo; char currentfilename[128] = { 0 }; DRAW_MODE draw_mode_pal = { 0, 0, 0, 0, 512, 256, 0, 16 }; DRAW_MODE draw_mode_ntsc = { 0, 0, 0, 8, 512, 240, 0, 0 }; DB MPBuff[2][2]; DB* last; DB* current; static CITYTYPE lasttype = (CITYTYPE)-1; static int lastcity = -1; int citystart[8]; XYPAIR citylumps[8][4]; #ifndef PSX int gContentOverride = 1; // use unpacked filesystem? char g_CurrentLevelFileName[64]; char* g_CurrentLevelSpoolData = NULL; #endif // !PSX // TODO: to game vars // [D] [T] void ClearMem(char* mem, int size) { // 16 bit aligned clear #ifndef PSX memset(mem, 0, size); #else char* end; end = mem + size; while ((((u_int)mem & 3) != 0 && (mem < end))) { *mem = 0; mem = (char*)((int)mem + 1); } while (mem <= end + -0x10) { *(u_int*)mem = 0; ((u_int*)mem)[1] = 0; ((u_int*)mem)[2] = 0; ((u_int*)mem)[3] = 0; mem = (char*)((u_int*)mem + 4); } while (mem <= end + -4) { *(u_int*)mem = 0; mem = (char*)((u_int*)mem + 1); } while (mem < end) { *mem = 0; mem = (char*)((int)mem + 1); } #endif // !PSX } // [D] [T] void setMem8(unsigned char* mem, unsigned char val, int size) { while (--size > 0) *mem++ = val; // TODO: check alignment/bounds? } // [D] [T] void setMem16(ushort* mem, ushort val, int size) { while (--size > 0) *mem++ = val; // TODO: check alignment/bounds? } // [T] void Init_FileSystem(void) { CdInit(); VSync(3); // Driver 1 were looking up level files on CD... } int gNumCDRetries = 0; // [D] [T] void DoCDRetry(void) { if (gNumCDRetries++ > 9) { CdInit(); VSync(3); gNumCDRetries = 0; } } // loads whole file into buffer // [D] [T] int Loadfile(char* name, char* addr) { int nread; unsigned char res[8]; char namebuffer[64]; #if USE_PC_FILESYSTEM int fileSize; sprintf(namebuffer, "%s%s", gDataFolder, name); FS_FixPathSlashes(namebuffer); FILE* fptr = fopen(namebuffer, "rb"); if (fptr) { fseek(fptr, 0, SEEK_END); fileSize = ftell(fptr); fseek(fptr, 0, SEEK_SET); int numRead = fread(addr, 1, fileSize, fptr); fclose(fptr); //SDL_Delay(200); // [A] PSX-like CD delay return numRead; } #endif #if USE_CD_FILESYSTEM return LoadfileSeg(name, addr, 0, -1); #endif } // [D] [T] int FileExists(char* filename) { char namebuffer[128]; if (*filename == '\0') return 0; #if USE_PC_FILESYSTEM sprintf(namebuffer, "%s%s", gDataFolder, filename); FS_FixPathSlashes(namebuffer); FILE* fp = fopen(namebuffer, "rb"); if (fp) { fclose(fp); return 1; } #endif // USE_PC_FILESYSTEM #if USE_CD_FILESYSTEM int retries; CdlFILE cdfile; sprintf(namebuffer, "\\%s%s;1", gDataFolder, filename); #ifdef PSX retries = 9; do { if (CdSearchFile(&cdfile, namebuffer) != NULL) return 1; retries--; DoCDRetry(); } while (retries >= 0); #else // don't retry or we'll have problems if(CdSearchFile(&cdfile, namebuffer) != NULL) return 1; #endif #endif // USE_CD_FILESYSTEM return 0; } // loads file partially into buffer // [D] [T] int LoadfileSeg(char* name, char* addr, int offset, int loadsize) { char namebuffer[64]; #if USE_PC_FILESYSTEM int fileSize; sprintf(namebuffer, "%s%s", gDataFolder, name); FS_FixPathSlashes(namebuffer); FILE* fptr = fopen(namebuffer, "rb"); if (fptr) { fseek(fptr, 0, SEEK_END); fileSize = ftell(fptr); if (loadsize == -1 || loadsize > fileSize) loadsize = fileSize; fseek(fptr, offset, SEEK_SET); int numRead = fread(addr, 1, loadsize, fptr); fclose(fptr); //SDL_Delay(200); // [A] PSX-like CD delay return numRead; } #endif // USE_PC_FILESYSTEM #if USE_CD_FILESYSTEM char* readPtr; char* sectorPtr; int remainingBytes; int remainingOffset; int sector; u_char result[8]; char sectorbuffer[CDSECTOR_SIZE]; CdlLOC pos; sprintf(namebuffer, "\\%s%s;1", gDataFolder, name); if (strcmp(currentfilename, namebuffer) != 0) { strcpy(currentfilename, namebuffer); while (CdSearchFile(¤tfileinfo, namebuffer) == NULL) { DoCDRetry(); } } if (loadsize == -1) loadsize = currentfileinfo.size - offset; remainingOffset = offset & 0x7ff; remainingBytes = loadsize; // seek to the sector sector = offset / CDSECTOR_SIZE + CdPosToInt(¤tfileinfo.pos); // start reading sectors from CD while(remainingBytes > 0) { // start reading if we getting to desired offset CdIntToPos(sector, &pos); // if we don't have offset or we have more than 2048 bytes // - we can read into buffer directly (which is faster) if (remainingBytes >= CDSECTOR_SIZE && remainingOffset == 0) sectorPtr = addr; else sectorPtr = sectorbuffer; // read sector do { if (CdDiskReady(0) != CdlComplete) DoCDRetry(); } while (CdControlB(CdlSetloc, (u_char*)&pos, NULL) == 0 || CdRead(1, (u_long*)sectorPtr, CdlModeSpeed) == 0 || CdReadSync(0, result) != 0); // non-direct reads must be handled if(sectorPtr == sectorbuffer) { // fetch readPtr = sectorbuffer + remainingOffset; // copy bytes while (remainingBytes > 0 && readPtr - sectorbuffer < CDSECTOR_SIZE) // don't leave boundary { *addr++ = *readPtr++; remainingBytes--; } remainingOffset = 0; } else { addr += CDSECTOR_SIZE; remainingBytes -= CDSECTOR_SIZE; } // go to next sector sector++; } return loadsize; #elif USE_PC_FILESYSTEM && !defined(__EMSCRIPTEN__) char errPrint[1024]; sprintf(errPrint, "Cannot open '%s'\n", namebuffer); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", errPrint, NULL); return 0; #endif } // [D] [T] void ReportMode(int on) { static u_char param[8]; if (XAPrepared() == 0) { if (on) param[0] = CdlModeSpeed | CdlModeRept; else param[0] = CdlModeSpeed; CdControlB(CdlSetmode, param, 0); } } static volatile u_char endread = 0; static volatile u_char load_complete = 0; // [D] [T] void data_ready(void) { if (endread) { CdDataCallback(NULL); load_complete = 1; } } static int current_sector = 0; // offset 0xAB27C static char* current_address = NULL; // offset 0xAB288 static int sectors_left = 0; // offset 0xAB280 // [D] [T] void sector_ready(u_char intr, u_char* result) { CdlLOC p; if (intr == 1) { // read sector data CdGetSector(current_address, SECTOR_SIZE); current_address += CDSECTOR_SIZE; current_sector++; sectors_left--; if (sectors_left == 0) { endread = 1; CdReadyCallback(NULL); CdControlF(CdlPause, 0); } } else { if (*result & CdlStatShellOpen) { CdReadyCallback(NULL); do { } while (CdDiskReady(1) != CdlComplete); CdReadyCallback(sector_ready); } CdIntToPos(current_sector, &p); CdControlF(CdlReadS, (u_char*)&p); } } #if USE_PC_FILESYSTEM // It has to be this way int loadsectorsPC(char* addr, int sector, int nsectors) { char namebuffer[64]; if (g_CurrentLevelFileName[0] == 0) return 0; strcpy(namebuffer, g_CurrentLevelFileName); FS_FixPathSlashes(namebuffer); FILE* fp = fopen(namebuffer, "rb"); if (fp) { // constrain nsectors to the maximum that can be read from the file int maxSectors; fseek(fp, 0, SEEK_END); maxSectors = (ftell(fp) - (sector * CDSECTOR_SIZE)) / CDSECTOR_SIZE; nsectors = MIN(nsectors, maxSectors); fseek(fp, sector * CDSECTOR_SIZE, SEEK_SET); fread(addr, CDSECTOR_SIZE, nsectors, fp); fclose(fp); ShowLoading(); return 1; } #if USE_PC_FILESYSTEM && !defined(__EMSCRIPTEN__) char errPrint[512]; sprintf(errPrint, "loadsectorsPC: failed to open '%s'\n", namebuffer); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", errPrint, NULL); #endif // USE_PC_FILESYSTEM && !PSX return 0; } #endif // USE_PC_FILESYSTEM // [D] [T] void loadsectors(char* addr, int sector, int nsectors) { CdlLOC pos; // [A] if (nsectors == 0) return; #if USE_PC_FILESYSTEM if (loadsectorsPC(addr, sector, nsectors)) return; #endif load_complete = 0; endread = 0; current_sector = sector; sectors_left = nsectors; current_address = addr; CdDataCallback(data_ready); CdReadyCallback(sector_ready); // start asynchronous reading... CdIntToPos(sector, &pos); CdControlF(CdlReadS, (u_char*)&pos); // ... but wait synchronously do { #ifdef __EMSCRIPTEN__ emscripten_sleep(0); #endif // __EMSCRIPTEN__ } while (load_complete == 0); ShowLoading(); } // [D] [T] void EnableDisplay(void) { int i; for (i = 0; i < NumPlayers; i++) // [A] { ClearOTagR((u_long*)MPBuff[i][0].ot, OTSIZE); ClearOTagR((u_long*)MPBuff[i][1].ot, OTSIZE); } } // [D] [T] void DisableDisplay(void) { SetDispMask(0); } int DoNotSwap = 0; DB* MPlast[2]; DB* MPcurrent[2]; // [A] void ClearCurrentDrawBuffers() { ClearOTagR((u_long*)current->ot, OTSIZE); current->primptr = current->primtab; } // [D] [T] void SwapDrawBuffers(void) { DrawSync(0); if (DoNotSwap == 0) { PutDispEnv(¤t->disp); } DoNotSwap = 0; PutDrawEnv(¤t->draw); DrawOTag((u_long*)(current->ot + OTSIZE-1)); if ((FrameCnt & 1) != 0) { current = &MPBuff[0][0]; last = &MPBuff[0][1]; } else { current = &MPBuff[0][1]; last = &MPBuff[0][0]; } ClearCurrentDrawBuffers(); } // [D] [T] void SwapDrawBuffers2(int player) { int toggle; DrawSync(0); if (player == 0) { PutDispEnv(¤t->disp); } PutDrawEnv(¤t->draw); DrawOTag((u_long*)(current->ot + OTSIZE - 1)); if (player == 1) { toggle = FrameCnt & 1; // [A] i guess it should work as intended MPcurrent[0] = &MPBuff[0][-toggle + 1]; MPlast[0] = &MPBuff[0][toggle]; MPcurrent[1] = &MPBuff[1][-toggle + 1]; MPlast[1] = &MPBuff[1][toggle]; } current = MPcurrent[1 - player]; last = MPlast[1 - player]; ClearOTagR((u_long*)current->ot, OTSIZE); current->primptr = current->primtab; } short paddp; short padd; // [D] [T] void UpdatePadData(void) { ReadControllers(); paddp = Pads[0].mapnew; padd = Pads[0].mapped; #ifndef PSX extern void SwitchMappings(int menu); SwitchMappings(pauseflag || CurrentGameMode == GAMEMODE_DIRECTOR); // FIXME: maybe gDrawPauseMenus instead of pauseflag? #endif } // [D] [T] void SetupDrawBuffers(void) { int i; RECT16 rect; // PAL: 256 // NTSC: 240 SetDefDispEnv(&MPBuff[0][0].disp, 0, 256, 320, SCREEN_H); SetDefDispEnv(&MPBuff[0][1].disp, 0, 0, 320, SCREEN_H); MPBuff[0][0].disp.screen.h = SCREEN_H; MPBuff[0][1].disp.screen.h = SCREEN_H; MPBuff[0][0].disp.screen.x = draw_mode.framex; MPBuff[0][1].disp.screen.x = draw_mode.framex; if (NoPlayerControl == 0) SetupDrawBufferData(NumPlayers); else SetupDrawBufferData(1); for (i = 0; i < 2; i++) { MPlast[i] = &MPBuff[i][1]; MPcurrent[i] = &MPBuff[i][0]; } rect.w = 320; rect.h = 512; rect.x = 0; rect.y = 0; current = MPcurrent[0]; last = MPlast[0]; ClearImage(&rect, 0, 0, 0); DrawSync(0); } // [D] [T] void SetupDrawBufferData(int num_players) { int i, j; int x[2], y[2]; int height; int toggle; if (num_players == 1) { height = SCREEN_H; // 240 on NTSC x[0] = 0; y[0] = 0; x[1] = 0; y[1] = 0; } else if (num_players == 2) { height = (SCREEN_H / 2 - 1); // 127; // 119 on NTSC x[0] = 0; y[0] = 0; x[1] = 0; y[1] = SCREEN_H / 2; // 120 on NTSC } else { D_CHECK_ERROR(true, "Erm... too many players selected. FATAL!"); } SetGeomOffset(320 / 2, height / 2); toggle = 0; for (i = 0; i < 2; i++) { for (j = 0; j < num_players; j++) { u_long* otpt; u_char* primpt; if (toggle) { otpt = (u_long*)_OT2; primpt = (u_char*)_primTab2; // _primTab1 + PRIMTAB_SIZE } else { otpt = (u_long*)_OT1; primpt = (u_char*)_primTab1; } toggle ^= 1; InitaliseDrawEnv(MPBuff[j], x[j], y[j], 320, height); MPBuff[i][j].primtab = (char*)primpt; MPBuff[i][j].primptr = (char*)primpt; MPBuff[i][j].ot = (OTTYPE*)otpt; } } #ifdef PAL_VERSION InitMatrix(aspect); aspect.m[1][1] = 4300; #else InitMatrix(aspect); aspect.m[1][1] = 4096; #endif } // [D] [T] void InitaliseDrawEnv(DB* pBuff, int x, int y, int w, int h) { #ifdef PSX #define DB1 pBuff[0] #define DB2 pBuff[1] #else // on PsyX we have to prevent flicker #define DB1 pBuff[1] #define DB2 pBuff[0] #endif SetDefDrawEnv(&DB1.draw, x, y, w, h); SetDefDrawEnv(&DB2.draw, x, y + 256, w, h); pBuff[0].id = 0; pBuff[0].draw.dfe = 1; pBuff[1].id = 1; pBuff[1].draw.dfe = 1; #ifdef USE_PGXP if(NumPlayers == 2) { pBuff[0].draw.clip.x -= 256; pBuff[0].draw.clip.w += 512; pBuff[1].draw.clip.x -= 256; pBuff[1].draw.clip.w += 512; } #endif } // [D] [T] void ResetCityType(void) { lasttype = (CITYTYPE)-1; lastcity = -1; #ifndef PSX free(g_CurrentLevelSpoolData); g_CurrentLevelSpoolData = NULL; #endif // PSX } // [A] CITYTYPE GetCityType() { return lasttype; } // [D] [T] void SetCityType(CITYTYPE type) { char* format; char filename[64]; unsigned char result[8]; if (type == lasttype && GameLevel == lastcity) return; ResetCityType(); lastcity = GameLevel; lasttype = type; #if USE_PC_FILESYSTEM // PC code switch (type) { case CITYTYPE_NIGHT: format = "%sN%s"; break; case CITYTYPE_MULTI_DAY: format = "%sM%s"; break; case CITYTYPE_MULTI_NIGHT: format = "%sMN%s"; break; default: format = "%s%s"; break; } sprintf(filename, format, gDataFolder, LevelFiles[GameLevel]); FS_FixPathSlashes(filename); FILE* levFp = fopen(filename, "rb"); if (levFp) { // store level name as it's required by loadsectorsPC strcpy(g_CurrentLevelFileName, filename); // spool position is forced to 0 citystart[GameLevel] = 0; // skip LUMP type (37) and size, it's already hardcoded fseek(levFp, 8, SEEK_CUR); fread(citylumps[GameLevel], 1, sizeof(citylumps[GameLevel]), levFp); fclose(levFp); return; } #endif // USE_PC_FILESYSTEM #if USE_CD_FILESYSTEM CdlFILE cdfile; int i; int sector; int* data; switch (type) { case CITYTYPE_NIGHT: format = "\\%sN%s;1"; break; case CITYTYPE_MULTI_DAY: format = "\\%sM%s;1"; break; case CITYTYPE_MULTI_NIGHT: format = "\\%sMN%s;1"; break; default: format = "\\%s%s;1"; break; } sprintf(filename, format, gDataFolder, LevelFiles[GameLevel]); while (CdSearchFile(&cdfile, filename) == NULL) { #if USE_PC_FILESYSTEM #ifndef __EMSCRIPTEN__ char errPrint[512]; sprintf(errPrint, "SetCityType: failed to open '%s'\n", filename); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", errPrint, NULL); exit(0); #endif // !__EMSCRIPTEN__ #else DoCDRetry(); #endif // USE_PC_FILESYSTEM } sector = CdPosToInt((CdlLOC*)&cdfile); citystart[GameLevel] = sector; do { do { if (CdDiskReady(0) != CdlComplete) DoCDRetry(); } while (CdControlB(CdlSetloc, (u_char*)&cdfile, 0) == 0 || CdRead(1, (u_long*)_OT1, CdlModeSpeed) == 0); } while (CdReadSync(0, result) != 0); data = (int*)_OT1 + 2; for (i = 0; i < 4; i++) { citylumps[GameLevel][i].x = data[0] + sector * CDSECTOR_SIZE; citylumps[GameLevel][i].y = data[1]; data += 2; } #elif USE_PC_FILESYSTEM && !defined(__EMSCRIPTEN__) char errPrint[1024]; sprintf(errPrint, "SetCityType: cannot open level '%s'\n", filename); SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "ERROR", errPrint, NULL); #endif // PSX } // [D] [T] CDTYPE DiscSwapped(char* filename) { #if 1//ndef PSX return CDTYPE_CORRECTDISC; #else CDTYPE ret; CdlFILE cdfile; switch(CdDiskReady(1)) { case CdlStatStandby: { switch(CdGetDiskType()) { case CdlStatNoDisk: ret = CDTYPE_NODISC; break; case CdlOtherFormat: ret = CDTYPE_WRONGDISC; break; case CdlCdromFormat: { if (CdSearchFile(&cdfile, filename) != NULL) ret = CDTYPE_CORRECTDISC; else ret = CDTYPE_DISCERROR; break; } } break; } case CdlStatShellOpen: ret = CDTYPE_SHELLOPEN; break; default: ret = CDTYPE_DISCERROR; } return ret; #endif }