diff --git a/src/date.c b/src/date.c index 4ad43362d9..7f6cc2eef3 100644 --- a/src/date.c +++ b/src/date.c @@ -32,6 +32,11 @@ int date_get_year(int months) return months / MONTH_COUNT; } +int date_get_total_months(int month, int year) +{ + return (year - 1) * MONTH_COUNT + month; +} + /** * * rct2: 0x006C4494 diff --git a/src/date.h b/src/date.h index e50d74ffaa..8bea447c45 100644 --- a/src/date.h +++ b/src/date.h @@ -34,6 +34,7 @@ enum { MONTH_COUNT }; +int date_get_total_months(int month, int year); void date_reset(); #endif \ No newline at end of file diff --git a/src/sawyercoding.c b/src/sawyercoding.c index f6efff4008..3e8214544e 100644 --- a/src/sawyercoding.c +++ b/src/sawyercoding.c @@ -35,35 +35,31 @@ static void decode_chunk_rotate(char *buffer, int length); int sawyercoding_read_chunk(HFILE hFile, uint8 *buffer) { DWORD numBytesRead; - int i, code; + sawyercoding_chunk_header chunkHeader; - uint8 encoding; - uint32 length; - - // Read chunk encoding and length - ReadFile(hFile, &encoding, 1, &numBytesRead, NULL); - ReadFile(hFile, &length, 4, &numBytesRead, NULL); + // Read chunk header + ReadFile(hFile, &chunkHeader, sizeof(sawyercoding_chunk_header), &numBytesRead, NULL); // Read chunk data - ReadFile(hFile, buffer, length, &numBytesRead, NULL); + ReadFile(hFile, buffer, chunkHeader.length, &numBytesRead, NULL); // Decode chunk data - switch (encoding) { + switch (chunkHeader.encoding) { case CHUNK_ENCODING_RLE: - length = decode_chunk_rle(buffer, length); + chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length); break; case CHUNK_ENCODING_RLECOMPRESSED: - length = decode_chunk_rle(buffer, length); - length = decode_chunk_repeat(buffer, length); + chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length); + chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length); break; case CHUNK_ENCODING_ROTATE: - decode_chunk_rotate(buffer, length); + decode_chunk_rotate(buffer, chunkHeader.length); break; } // Set length - RCT2_GLOBAL(0x009E3828, uint32) = length; - return length; + RCT2_GLOBAL(0x009E3828, uint32) = chunkHeader.length; + return chunkHeader.length; } /** diff --git a/src/sawyercoding.h b/src/sawyercoding.h index 52b893504c..71652218f0 100644 --- a/src/sawyercoding.h +++ b/src/sawyercoding.h @@ -22,6 +22,12 @@ #define _SAWYERCODING_H_ #include +#include "rct2.h" + +typedef struct { + uint8 encoding; + uint32 length; +} sawyercoding_chunk_header; enum { CHUNK_ENCODING_NONE, diff --git a/src/scenario.c b/src/scenario.c index 99e9ead251..bed0b8d1d7 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -33,6 +33,7 @@ #define RCT2_NUM_SCENARIOS RCT2_GLOBAL(RCT2_ADDRESS_NUM_SCENARIOS, sint32) int _scenarioListSize; +static void scenario_list_add(char *path); static void scenario_list_sort(); static int scenario_list_sort_compare(const void* a, const void* b); static void scenario_scores_load(); @@ -63,99 +64,109 @@ static rct_scenario_basic *get_scenario_by_filename(char *filename) */ void scenario_load_list() { + int i; HANDLE hFindFile; WIN32_FIND_DATAA findFileData; - int i; // Load scores scenario_scores_load(); - // Unset flag 1 for each scenario + // Set all scenarios to be invisible for (i = 0; i < RCT2_NUM_SCENARIOS; i++) - RCT2_SCENARIO_LIST[i].var_0268 &= ~0x01; + RCT2_SCENARIO_LIST[i].flags &= ~SCENARIO_FLAGS_VISIBLE; // Enumerate through each scenario in the directory hFindFile = FindFirstFile(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), &findFileData); if (hFindFile != INVALID_HANDLE_VALUE) { do { - // Check if scenario already exists in list, likely if in scores - rct_scenario_basic *scenario = get_scenario_by_filename(findFileData.cFileName); - if (scenario != NULL) { - // Set 0141EF68 to the scenario path - subsitute_path( - RCT2_ADDRESS(0x0141EF68, char), - RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), - findFileData.cFileName - ); - - // Load the basic scenario information - if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) - continue; - - // - if (RCT2_GLOBAL(0x0141F570, sint8) != -1) - continue; - - // Update the scenario information - scenario->var_0268 |= 0x01; - scenario->category = RCT2_GLOBAL(0x0141F571, uint8); - scenario->var_0120 = RCT2_GLOBAL(0x0141F572, sint8); - scenario->var_0121 = RCT2_GLOBAL(0x0141F573, sint8); - scenario->var_0122 = RCT2_GLOBAL(0x0141F574, sint32); - scenario->var_0126 = RCT2_GLOBAL(0x0141F578, sint16); - strcpy(scenario->name, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char)); - strcpy(scenario->details, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char)); - continue; - } - - // Check if the scenario list buffer has room for another scenario - if ((RCT2_NUM_SCENARIOS + 1) * sizeof(rct_scenario_basic) > _scenarioListSize) { - // Allocate more room - _scenarioListSize += 16 * sizeof(rct_scenario_basic); - RCT2_SCENARIO_LIST = (rct_scenario_basic*)rct2_realloc(RCT2_SCENARIO_LIST, _scenarioListSize); - } - - // Set 0141EF68 to the scenario path - subsitute_path( - RCT2_ADDRESS(0x0141EF68, char), - RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), - findFileData.cFileName - ); - - // Load the scenario information - if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) - continue; - - // - if (RCT2_GLOBAL(0x0141F570, sint8) != -1) - continue; - - // Increment the number of scenarios - i = RCT2_NUM_SCENARIOS; - RCT2_NUM_SCENARIOS++; - - // Add this new scenario to the list - strcpy(RCT2_SCENARIO_LIST[i].path, findFileData.cFileName); - RCT2_SCENARIO_LIST[i].var_0268 = 0x01; - if (RCT2_GLOBAL(0x009AA00C, uint8) & 1) - RCT2_SCENARIO_LIST[i].var_0268 |= 0x04; - RCT2_SCENARIO_LIST[i].category = RCT2_GLOBAL(0x0141F571, uint8); - RCT2_SCENARIO_LIST[i].var_0120 = RCT2_GLOBAL(0x0141F572, sint8); - RCT2_SCENARIO_LIST[i].var_0121 = RCT2_GLOBAL(0x0141F573, sint8); - RCT2_SCENARIO_LIST[i].var_0122 = RCT2_GLOBAL(0x0141F574, sint32); - RCT2_SCENARIO_LIST[i].var_0126 = RCT2_GLOBAL(0x0141F578, sint16); - strcpy(RCT2_SCENARIO_LIST[i].name, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char)); - strcpy(RCT2_SCENARIO_LIST[i].details, RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char)); + scenario_list_add(findFileData.cFileName); } while (FindNextFile(hFindFile, &findFileData)); FindClose(hFindFile); } + // Sort alphabetically scenario_list_sort(); // Save the scores scenario_scores_save(); } +static void scenario_list_add(char *path) +{ + int i; + rct_scenario_basic *scenario; + rct_s6_info *s6Info = 0x0141F570; + + // Check if scenario already exists in list, likely if in scores + scenario = get_scenario_by_filename(path); + if (scenario != NULL) { + // Set 0141EF68 to the scenario path + subsitute_path( + RCT2_ADDRESS(0x0141EF68, char), + RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), + path + ); + + // Load the basic scenario information + if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) + return; + + // + if (s6Info->var_000 != 255) + return; + + // Update the scenario information + scenario->flags |= SCENARIO_FLAGS_VISIBLE; + scenario->category = s6Info->category; + scenario->objective_type = s6Info->objective_type; + scenario->objective_arg_1 = s6Info->objective_arg_1; + scenario->objective_arg_2 = s6Info->objective_arg_2; + scenario->objective_arg_3 = s6Info->objective_arg_3; + strcpy(scenario->name, s6Info->name); + strcpy(scenario->details, s6Info->details); + return; + } + + // Check if the scenario list buffer has room for another scenario + if ((RCT2_NUM_SCENARIOS + 1) * sizeof(rct_scenario_basic) > _scenarioListSize) { + // Allocate more room + _scenarioListSize += 16 * sizeof(rct_scenario_basic); + RCT2_SCENARIO_LIST = (rct_scenario_basic*)rct2_realloc(RCT2_SCENARIO_LIST, _scenarioListSize); + } + + // Set 0141EF68 to the scenario path + subsitute_path( + RCT2_ADDRESS(0x0141EF68, char), + RCT2_ADDRESS(RCT2_ADDRESS_SCENARIOS_PATH, char), + path + ); + + // Load the scenario information + if (!scenario_load_basic(RCT2_ADDRESS(0x0141EF68, char))) + return; + + // + if (s6Info->var_000 != 255) + return; + + // Increment the number of scenarios + scenario = &RCT2_SCENARIO_LIST[RCT2_NUM_SCENARIOS]; + RCT2_NUM_SCENARIOS++; + + // Add this new scenario to the list + strcpy(scenario->path, path); + scenario->flags = SCENARIO_FLAGS_VISIBLE; + if (RCT2_GLOBAL(0x009AA00C, uint8) & 1) + scenario->flags |= SCENARIO_FLAGS_SIXFLAGS; + scenario->category = s6Info->category; + scenario->objective_type = s6Info->objective_type; + scenario->objective_arg_1 = s6Info->objective_arg_1; + scenario->objective_arg_2 = s6Info->objective_arg_2; + scenario->objective_arg_3 = s6Info->objective_arg_3; + strcpy(scenario->name, s6Info->name); + strcpy(scenario->details, s6Info->details); +} + /** * Sort the list of scenarios. This used to be an insertion sort which took * place as each scenario loaded. It has now been changed to a quicksort which @@ -248,17 +259,22 @@ static int scenario_load_basic(char *path) { HANDLE hFile; int _eax; + rct_s6_header *s6Header = 0x009E34E4; + rct_s6_info *s6Info = 0x0141F570; hFile = CreateFile(path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS | FILE_ATTRIBUTE_NORMAL, NULL); if (hFile != INVALID_HANDLE_VALUE) { RCT2_GLOBAL(0x009E382C, HANDLE*) = hFile; - sawyercoding_read_chunk(hFile, 0x009E34E4); - if (RCT2_GLOBAL(0x009E34E4, uint8) == 1) { - sawyercoding_read_chunk(hFile, 0x0141F570); + + // Read first chunk + sawyercoding_read_chunk(hFile, s6Header); + if (s6Header->type == S6_TYPE_SCENARIO) { + // Read second chunk + sawyercoding_read_chunk(hFile, s6Info); CloseHandle(hFile); RCT2_GLOBAL(0x009AA00C, uint8) = 0; - if (RCT2_GLOBAL(0x0141F6F8, uint8) != 255) { + if (s6Info->flags != 255) { __asm { push ebp mov ebp, 0141F6F8h @@ -270,8 +286,8 @@ static int scenario_load_basic(char *path) } int ebp = RCT2_GLOBAL(0x009ADAF8, uint32); - format_string(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_NAME, char), RCT2_GLOBAL(ebp, sint16), NULL); - format_string(RCT2_ADDRESS(RCT2_ADDRESS_SCENARIO_DETAILS, char), RCT2_GLOBAL(ebp + 4, sint16), NULL); + format_string(s6Info->name, RCT2_GLOBAL(ebp, sint16), NULL); + format_string(s6Info->details, RCT2_GLOBAL(ebp + 4, sint16), NULL); RCT2_GLOBAL(0x009AA00C, uint8) = RCT2_GLOBAL(ebp + 6, uint8); RCT2_CALLPROC(0x006A982D); __asm mov _eax, eax diff --git a/src/scenario.h b/src/scenario.h index 760c6b05a6..c64f125c3d 100644 --- a/src/scenario.h +++ b/src/scenario.h @@ -23,6 +23,37 @@ #include "rct2.h" +/** + * SV6/SC6 header chunk + * size: 0x20 + */ +typedef struct { + uint16 type; // 0x00 + uint16 num_packed_objects; // 0x02 + uint32 version; // 0x04 + uint32 magic_number; // 0x08 + uint8 pad_0C[0x14]; +} rct_s6_header; + +/** + * SC6 information chunk + * size: 0x198 + */ +typedef struct { + uint8 var_000; + uint8 category; // 0x01 + uint8 objective_type; // 0x02 + uint8 objective_arg_1; // 0x03 + sint32 objective_arg_2; // 0x04 + sint16 objective_arg_3; // 0x08 + uint8 pad_00A[0x3E]; + char name[64]; // 0x48 + char details[256]; // 0x88 + uint8 flags; // 0x188 + uint8 pad_189[0x0F]; +} rct_s6_info; + + /** * Scenario basic structure, mainly for scenario select * size: 0x02B0 @@ -31,17 +62,36 @@ typedef struct { char path[256]; // 0x0000 uint8 category; // 0x0100 uint8 pad_0101[0x1F]; - sint8 var_0120; - sint8 var_0121; - sint32 var_0122; - sint16 var_0126; + sint8 objective_type; // 0x0120 + sint8 objective_arg_1; // 0x0121 + sint32 objective_arg_2; // 0x0122 + sint16 objective_arg_3; // 0x0126 char name[64]; // 0x0128 char details[256]; // 0x0168 - sint32 var_0268; - uint32 pad_026C; - sint8 var_0270[64]; + sint32 flags; // 0x0268 + uint32 company_value; // 0x026C + char completed_by[64]; // 0x0270 } rct_scenario_basic; +enum { + SCENARIO_FLAGS_VISIBLE = (1 << 0), + SCENARIO_FLAGS_COMPLETED = (1 << 1), + SCENARIO_FLAGS_SIXFLAGS = (1 << 2) +}; + +enum { + S6_TYPE_SAVEDGAME, + S6_TYPE_SCENARIO +}; + +enum { + SCENARIO_CATEGORY_BEGINNER, + SCENARIO_CATEGORY_CHALLENGING, + SCENARIO_CATEGORY_EXPERT, + SCENARIO_CATEGORY_REAL, + SCENARIO_CATEGORY_BUILDYOUROWN +}; + enum { OBJECTIVE_NONE, OBJECTIVE_GUESTS_BY, diff --git a/src/window_title_scenarioselect.c b/src/window_title_scenarioselect.c index 0d5f85c890..f0f4369a64 100644 --- a/src/window_title_scenarioselect.c +++ b/src/window_title_scenarioselect.c @@ -20,6 +20,7 @@ #include "addresses.h" #include "audio.h" +#include "date.h" #include "scenario.h" #include "strings.h" #include "sprites.h" @@ -147,10 +148,8 @@ static void window_scenarioselect_init_tabs() show_pages = 0; for (i = 0; i < RCT2_GLOBAL(RCT2_ADDRESS_NUM_SCENARIOS, sint32); i++) { scenario = &(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*)[i]); - if (!(scenario->var_0268 & 1)) - continue; - - show_pages |= 1 << scenario->category; + if (scenario->flags & SCENARIO_FLAGS_VISIBLE) + show_pages |= 1 << scenario->category; } x = 3; @@ -212,10 +211,8 @@ static void window_scenarioselect_scrollgetsize() scenario = &(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*)[i]); if (scenario->category != w->var_4AC) continue; - if (!(scenario->var_0268 & 1)) - continue; - - height += 24; + if (scenario->flags & SCENARIO_FLAGS_VISIBLE) + height += 24; } __asm mov ecx, 0 @@ -237,7 +234,7 @@ static void window_scenarioselect_scrollmousedown() scenario = &(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*)[i]); if (scenario->category != w->var_4AC) continue; - if (!(scenario->var_0268 & 1)) + if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE)) continue; y -= 24; @@ -266,7 +263,7 @@ static void window_scenarioselect_scrollmouseover() scenario = &(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*)[i]); if (scenario->category != w->var_4AC) continue; - if (!(scenario->var_0268 & 1)) + if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE)) continue; y -= 24; @@ -298,6 +295,7 @@ static void window_scenarioselect_paint() rct_window *w; rct_drawpixelinfo *dpi; rct_widget *widget; + rct_scenario_basic *scenario; __asm mov w, esi __asm mov dpi, edi @@ -317,40 +315,41 @@ static void window_scenarioselect_paint() } // Return if no scenario highlighted - if (w->var_494 == NULL) + scenario = (rct_scenario_basic*)w->var_494; + if (scenario == NULL) return; // Draw SixFlags image - if (*((int*)(w->var_494 + 0x0268)) & 4) + if (scenario->flags & SCENARIO_FLAGS_SIXFLAGS) gfx_draw_sprite(dpi, SPR_SIX_FLAGS, w->x + w->width - 55, w->y + w->height - 75); // Scenario name x = w->x + window_scenarioselect_widgets[WIDX_SCENARIOLIST].right + 4; y = w->y + window_scenarioselect_widgets[WIDX_TABCONTENT].top + 5; - strcpy(0x009BC677, (char*)(w->var_494 + 0x0128)); + strcpy(0x009BC677, scenario->name); *((short*)(0x0013CE952 + 0)) = 3165; gfx_draw_string_centred_clipped(dpi, 1193, (void*)0x013CE952, 0, x + 85, y, 170); y += 15; - // Scenario description - strcpy(0x009BC677, (char*)(w->var_494 + 0x0168)); + // Scenario details + strcpy(0x009BC677, scenario->details); *((short*)(0x0013CE952 + 0)) = 3165; y += gfx_draw_string_left_wrapped(dpi, (void*)0x013CE952, x, y, 170, 1191, 0) + 5; // Scenario objective - *((short*)(0x0013CE952 + 0)) = *((unsigned char*)(w->var_494 + 0x0120)) + STR_OBJECTIVE_NONE; - *((short*)(0x0013CE952 + 2)) = *((short*)(w->var_494 + 0x0126)); - *((short*)(0x0013CE956 + 0)) = *((unsigned char*)(w->var_494 + 0x0121)) * 8 - 1; - *((int*)(0x0013CE956 + 2)) = *((int*)(w->var_494 + 0x0122)); + *((short*)(0x0013CE952 + 0)) = scenario->objective_type + STR_OBJECTIVE_NONE; + *((short*)(0x0013CE952 + 2)) = scenario->objective_arg_3; + *((short*)(0x0013CE952 + 4)) = date_get_total_months(MONTH_OCTOBER, scenario->objective_arg_1); + *((int*)(0x0013CE952 + 6)) = scenario->objective_arg_2; y += gfx_draw_string_left_wrapped(dpi, (void*)0x013CE952, x, y, 170, STR_OBJECTIVE, 0) + 5; // Scenario score - if (!(*((int*)(w->var_494 + 0x0268)) & 2)) - return; - strcpy(0x009BC677, (char*)(w->var_494 + 0x0270)); - *((short*)(0x0013CE952 + 0)) = 3165; - *((int*)(0x0013CE952 + 2)) = *((int*)(w->var_494 + 0x026C)); - y += gfx_draw_string_left_wrapped(dpi, (void*)0x013CE952, x, y, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, 0); + if (scenario->flags & SCENARIO_FLAGS_COMPLETED) { + strcpy(0x009BC677, scenario->completed_by); + *((short*)(0x0013CE952 + 0)) = 3165; + *((int*)(0x0013CE952 + 2)) = scenario->company_value; + y += gfx_draw_string_left_wrapped(dpi, (void*)0x013CE952, x, y, 170, STR_COMPLETED_BY_WITH_COMPANY_VALUE, 0); + } } static void window_scenarioselect_scrollpaint() @@ -372,7 +371,7 @@ static void window_scenarioselect_scrollpaint() scenario = &(RCT2_GLOBAL(RCT2_ADDRESS_SCENARIO_LIST, rct_scenario_basic*)[i]); if (scenario->category != w->var_4AC) continue; - if (!(scenario->var_0268 & 1)) + if (!(scenario->flags & SCENARIO_FLAGS_VISIBLE)) continue; if (y > dpi->y + dpi->height) @@ -390,12 +389,12 @@ static void window_scenarioselect_scrollpaint() gfx_draw_string_centred(dpi, highlighted ? 1193 : 1191, 210, y + 1, 0, (void*)0x013CE952); // Check if scenario is completed - if (scenario->var_0268 & 2) { + if (scenario->flags & SCENARIO_FLAGS_COMPLETED) { // Draw completion tick gfx_draw_sprite(dpi, 0x5A9F, 395, y + 1); // Draw completion score - strcpy((char*)0x009BC677, scenario->var_0270); + strcpy((char*)0x009BC677, scenario->completed_by); *((short*)0x013CE952) = 2793; *((short*)0x013CE954) = 3165; gfx_draw_string_centred(dpi, highlighted ? 1193 : 1191, 210, y + 11, 0, (void*)0x013CE952);