diff --git a/src/config.c b/src/config.c index 41551d94a5..c82a4cb8b1 100644 --- a/src/config.c +++ b/src/config.c @@ -125,7 +125,7 @@ void config_load() { FILE *fp=NULL; - char* path = get_file_path(PATH_ID_GAMECFG); + const char *path = get_file_path(PATH_ID_GAMECFG); fp = fopen(path, "rb"); diff --git a/src/rct2.c b/src/rct2.c index 5f18db7805..559fced6bf 100644 --- a/src/rct2.c +++ b/src/rct2.c @@ -232,9 +232,16 @@ void subsitute_path(char *dest, const char *path, const char *filename) // rct2: 0x00674B42 void rct2_startup_checks() { - // check if game is already running + // Check if game is already running + if (check_mutex()) + { + RCT2_ERROR("Game is already running"); + RCT2_CALLPROC_X(0x006E3838, 0x343, 0xB2B, 0, 0, 0, 0, 0); // exit_with_error + } - RCT2_CALLPROC_EBPSAFE(0x00674C0B); + // Check data files + check_file_paths(); + check_files_integrity(); } void rct2_update() @@ -305,6 +312,99 @@ void check_cmdline_arg() } } +// rct2: 0x00407DB0 +int check_mutex() +{ + const char * const mutex_name = "RollerCoaster Tycoon 2_GSKMUTEX"; // rct2 @ 0x009AAC3D + 0x009A8B50 + + HANDLE mutex = OpenMutex(MUTEX_ALL_ACCESS, FALSE, mutex_name); + + if (mutex != NULL) + { + // Already running + CloseHandle(mutex); + return 1; + } + + HANDLE status = CreateMutex(NULL, FALSE, mutex_name); + return 0; +} + +// rct2: 0x00674C95 +void check_file_paths() +{ + for (int pathId = 0; pathId < PATH_ID_END; pathId += 1) + { + check_file_path(pathId); + } +} + +// rct2: 0x00674CA5 +void check_file_path(int pathId) +{ + const char * path = get_file_path(pathId); + HANDLE file = CreateFile(path, FILE_GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); + + switch (pathId) + { + case PATH_ID_GAMECFG: + case PATH_ID_SCORES: + // Do nothing; these will be created later if they do not exist yet + break; + + case PATH_ID_CUSTOM1: + if (file != INVALID_HANDLE_VALUE) + RCT2_GLOBAL(0x009AF164, unsigned int) = SetFilePointer(file, 0, 0, FILE_END); // Store file size in music_custom1_size @ 0x009AF164 + break; + + case PATH_ID_CUSTOM2: + if (file != INVALID_HANDLE_VALUE) + RCT2_GLOBAL(0x009AF16E, unsigned int) = SetFilePointer(file, 0, 0, FILE_END); // Store file size in music_custom2_size @ 0x009AF16E + break; + + default: + if (file == INVALID_HANDLE_VALUE) { + // A data file is missing from the installation directory. The original implementation + // asks for a CD-ROM path at this point and stores it in cdrom_path @ 0x9AA318. + // The file_on_cdrom[pathId] @ 0x009AA0B flag is set to 1 as well. + // For PATH_ID_SIXFLAGS_MAGICMOUNTAIN and PATH_ID_SIXFLAGS_BUILDYOUROWN, + // the original implementation always assumes they are stored on CD-ROM. + // This has been removed for now for the sake of simplicity and could be added + // later in a more convenient way using the INI file. + RCT2_ERROR("Could not find file %s", path); + RCT2_CALLPROC_X(0x006E3838, 0x343, 0x337, 0, 0, 0, 0, 0); // exit_with_error + } + break; + } + + if (file != INVALID_HANDLE_VALUE) + CloseHandle(file); +} + +// rct2: 0x00674C0B +void check_files_integrity() +{ + int i = 0; + while (files_to_check[i].pathId != PATH_ID_END) + { + WIN32_FIND_DATA find_data; + const char * path = get_file_path(files_to_check[i].pathId); + HANDLE file = FindFirstFile(path, &find_data); + + if (file == INVALID_HANDLE_VALUE || find_data.nFileSizeLow != files_to_check[i].fileSize) + { + if (file != INVALID_HANDLE_VALUE) + FindClose(file); + RCT2_ERROR("Integrity check failed for %s", path); + RCT2_CALLPROC_X(0x006E3838, 0x343, 0x337, 0, 0, 0, 0, 0); // exit_with_error + } + + FindClose(file); + + i += 1; + } +} + void rct2_update_2() { int tick, tick2; @@ -346,13 +446,39 @@ void rct2_endupdate() * * rct2: 0x00674E6C */ -char *get_file_path(int pathId) +const char *get_file_path(int pathId) { - int eax, ebx, ecx, edx, esi, edi, ebp; + static char path[MAX_PATH]; // get_file_path_buffer @ 0x009E3605 - ebx = pathId; - RCT2_CALLFUNC_X(0x00674E6C, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return (char*)ebx; + // The original implementation checks if the file is on CD-ROM here (file_on_cdrom[pathId] @ 0x009AA0B1). + // If so, the CD-ROM path (cdrom_path @ 0x9AA318) is used instead. This has been removed for now for + // the sake of simplicity. + strcpy(path, gGeneral_config.game_path); + + // Make sure base path is terminated with a slash + if (strlen(path) == 0 || path[strlen(path) - 1] != '\\') + { + if (strlen(path) >= MAX_PATH - 1) + { + RCT2_ERROR("Path for %s too long", file_paths[pathId]); + path[0] = '\0'; + return path; + } + + strcat(path, "\\"); + } + + // Concatenate file path + if (strlen(path) + strlen(file_paths[pathId]) > MAX_PATH) + { + RCT2_ERROR("Path for %s too long", file_paths[pathId]); + path[0] = '\0'; + return path; + } + + strcat(path, file_paths[pathId]); + + return path; } /** diff --git a/src/rct2.h b/src/rct2.h index f5b765b206..dcdb81bcc0 100644 --- a/src/rct2.h +++ b/src/rct2.h @@ -157,13 +157,116 @@ enum { PATH_ID_CSS43, PATH_ID_CSS44, PATH_ID_CSS45, - PATH_ID_CSS46 + PATH_ID_CSS46, + PATH_ID_END +}; +// rct2 @ 0x0097F67C +static const char * const file_paths[] = +{ + "Data\\G1.DAT", + "Data\\PLUGIN.DAT", + "Data\\CSS1.DAT", + "Data\\CSS2.DAT", + "Data\\CSS4.DAT", + "Data\\CSS5.DAT", + "Data\\CSS6.DAT", + "Data\\CSS7.DAT", + "Data\\CSS8.DAT", + "Data\\CSS9.DAT", + "Data\\CSS10.DAT", + "Data\\CSS11.DAT", + "Data\\CSS12.DAT", + "Data\\CSS13.DAT", + "Data\\CSS14.DAT", + "Data\\CSS15.DAT", + "Data\\CSS16.DAT", + "Data\\CSS3.DAT", + "Data\\GAME.CFG", + "Data\\TUT640A.DAT", + "Data\\TUT640B.DAT", + "Data\\TUT640C.DAT", + "Data\\TUT800A.DAT", + "Data\\TUT800B.DAT", + "Data\\TUT800C.DAT", + "Data\\KANJI.DAT", + "Data\\CSS17.DAT", + "Data\\CSS18.DAT", + "Data\\CSS19.DAT", + "Data\\CSS20.DAT", + "Data\\CSS21.DAT", + "Data\\CSS22.DAT", + "Saved Games\\scores.DAT", + "Data\\CSS23.DAT", + "Data\\CSS24.DAT", + "Data\\CSS25.DAT", + "Data\\CSS26.DAT", + "Data\\CSS27.DAT", + "Data\\CSS28.DAT", + "Data\\CSS29.DAT", + "Data\\CSS30.DAT", + "Data\\CSS31.DAT", + "Data\\CSS32.DAT", + "Data\\CSS33.DAT", + "Data\\CSS34.DAT", + "Data\\CSS35.DAT", + "Data\\CSS36.DAT", + "Data\\CSS37.DAT", + "Data\\CSS38.DAT", + "Data\\CUSTOM1.WAV", + "Data\\CUSTOM2.WAV", + "Data\\CSS39.DAT", + "Data\\CSS40.DAT", + "Tracks\\Tracks.IDX", + "Data\\CSS41.DAT", + "Scenarios\\Six Flags Magic Mountain.SC6", + "Scenarios\\Build your own Six Flags Park.SC6", + "Data\\CSS42.DAT", + "Data\\CSS43.DAT", + "Data\\CSS44.DAT", + "Data\\CSS45.DAT", + "Data\\CSS46.DAT" +}; + +// Files to check (rct2 @ 0x0097FB5A) +static const struct file_to_check +{ + int pathId; // ID of file + unsigned int fileSize; // Expected size in bytes +} files_to_check[] = { + { PATH_ID_CSS18, 8429568 }, + { PATH_ID_CSS19, 10143784 }, + { PATH_ID_CSS20, 12271656 }, + { PATH_ID_CSS21, 9680968 }, + { PATH_ID_CSS22, 10062056 }, + { PATH_ID_CSS23, 11067432 }, + { PATH_ID_CSS24, 12427456 }, + { PATH_ID_CSS25, 15181512 }, + { PATH_ID_CSS26, 10694816 }, + { PATH_ID_CSS27, 10421232 }, + { PATH_ID_CSS28, 13118376 }, + { PATH_ID_CSS29, 15310892 }, + { PATH_ID_CSS30, 10215464 }, + { PATH_ID_CSS31, 11510316 }, + { PATH_ID_CSS32, 11771944 }, + { PATH_ID_CSS33, 10759724 }, + { PATH_ID_CSS34, 14030716 }, + { PATH_ID_CSS35, 11642576 }, + { PATH_ID_CSS36, 8953764 }, + { PATH_ID_CSS37, 13303852 }, + { PATH_ID_CSS38, 10093888 }, + { PATH_ID_CSS39, 7531564 }, + { PATH_ID_CSS40, 5291306 }, + { PATH_ID_END, 0 } }; void rct2_endupdate(); void subsitute_path(char *dest, const char *path, const char *filename); -char *get_file_path(int pathId); +int check_mutex(); +void check_file_paths(); +void check_file_path(int pathId); +void check_files_integrity(); +const char *get_file_path(int pathId); void get_system_info(); void get_system_time(); void get_local_time();