diff --git a/src/game.c b/src/game.c index 9186269bb7..8348b053cd 100644 --- a/src/game.c +++ b/src/game.c @@ -840,7 +840,7 @@ int game_load_save() if (s6Header->num_packed_objects > 0) { j = 0; for (i = 0; i < s6Header->num_packed_objects; i++) - j += object_load_packed(); + j += object_load_packed(file); if (j > 0) object_list_load(); } diff --git a/src/object.c b/src/object.c index 24798d7fdb..dae1f19074 100644 --- a/src/object.c +++ b/src/object.c @@ -20,17 +20,276 @@ #include #include +#include +#include #include "addresses.h" +#include "string_ids.h" #include "object.h" +#include "osinterface.h" #include "sawyercoding.h" +int object_entry_compare(rct_object_entry *a, rct_object_entry *b); +int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength); +int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp); +rct_object_entry *object_get_next(rct_object_entry *entry); /** * * rct2: 0x006A985D */ int object_load(int groupIndex, rct_object_entry *entry) { - return !(RCT2_CALLPROC_X(0x006A985D, 0, 0, groupIndex, 0, 0, 0, (int)entry) & 0x100); + RCT2_GLOBAL(0xF42B64, uint32) = groupIndex; + + //part of 6a9866 + rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); + + if (!(RCT2_GLOBAL(0xF42B6C, uint32))){ + RCT2_GLOBAL(0xF42BD9, uint8) = 0; + return 1; + } + for (int i = 0; i < RCT2_GLOBAL(0x00F42B6C, sint32); i++) { + if (object_entry_compare(installedObject, entry)){ + + char path[260]; + char *objectPath = (char*)installedObject + 16; + subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath); + rct_object_entry openedEntry; + FILE *file = fopen(path, "rb"); + if (file != NULL) { + fread(&openedEntry, sizeof(rct_object_entry), 1, file); + if (object_entry_compare(&openedEntry, entry)) { + + // Get chunk size + char *pos = (char*)installedObject + 16; + do { + pos++; + } while (*(pos - 1) != 0); + + // Read chunk + int chunkSize = *((uint32*)pos); + char *chunk; + + if (chunkSize == 0xFFFFFFFF) { + chunk = rct2_malloc(0x600000); + chunkSize = sawyercoding_read_chunk(file, chunk); + chunk = rct2_realloc(chunk, chunkSize); + } + else { + chunk = rct2_malloc(chunkSize); + chunkSize = sawyercoding_read_chunk(file, chunk); + } + fclose(file); + + // Calculate and check checksum + if (object_calculate_checksum(&openedEntry, chunk, chunkSize) != openedEntry.checksum) { + RCT2_GLOBAL(0x00F42BD9, uint8) = 2; + rct2_free(chunk); + return 0; + } + + if (object_paint(openedEntry.flags & 0x0F, 2, 0, openedEntry.flags & 0x0F, 0, (int)chunk, 0, 0)) { + RCT2_GLOBAL(0x00F42BD9, uint8) = 3; + rct2_free(chunk); + return 0; + } + + int yyy = RCT2_GLOBAL(0x009ADAF0, uint32); + + if (yyy >= 0x4726E){ + RCT2_GLOBAL(0x00F42BD9, uint8) = 4; + rct2_free(chunk); + return 0; + } + //B84 is openedEntry + int ebp = openedEntry.flags & 0x0F; + int esi = RCT2_ADDRESS(0x98D97C, uint32)[ebp * 2]; + int ecx = groupIndex; + if (ecx == -1){ + for (int ecx = 0; ((sint32*)esi)[ecx] != -1; ecx++){ + if ((ecx + 1) >= object_entry_group_counts[ebp]){ + RCT2_GLOBAL(0x00F42BD9, uint8) = 5; + rct2_free(chunk); + return 0; + } + } + } + ((char**)esi)[ecx] = chunk; + + int* edx = (int*)( ecx * 20 + RCT2_ADDRESS(0x98D980, uint32)[ebp * 2]); + memcpy(edx, (int*)&openedEntry, 20); + if (RCT2_GLOBAL(0x9ADAFD, uint8) == 0)return 1; + object_paint(ecx, 0, ecx, ebp, 0, (int)chunk, 0, 0); + return 1; + } + fclose(file); + } + } + installedObject = object_get_next(installedObject); + } + //6a991f + // Installed Object can not be found. + return 0; + //return !(RCT2_CALLPROC_X(0x006A985D, 0, 0, groupIndex, 0, 0, 0, (int)entry) & 0x400); +} + +/** rct2: 0x006a9f42 + * ebx : file + * ebp : entry + */ +int sub_6A9F42(FILE *file, rct_object_entry* entry){ + int eax = 0, entryGroupIndex = 0, type = 0, edx = 0, edi = 0, ebp = (int)entry, chunk = 0; + RCT2_CALLFUNC_X(0x6A9DA2, &eax, &entryGroupIndex, &type, &edx, &chunk, &edi, &ebp); + if (eax == 0) return 0; + + object_paint(type, 1, entryGroupIndex, type, edx, chunk, edi, ebp); + + + rct_object_entry* installed_entry = (rct_object_entry*)(entryGroupIndex * 20 + RCT2_ADDRESS(0x98D980, uint32)[type * 2]); + uint8* dst_buffer = malloc(0x600000); + memcpy(dst_buffer, (void*)installed_entry, 16); + + uint32 size_dst = 16; + + sawyercoding_chunk_header chunkHeader; + // Encoding type (not used anymore) + RCT2_GLOBAL(0x9E3CBD, uint8) = object_entry_group_encoding[type]; + + chunkHeader.encoding = object_entry_group_encoding[type]; + chunkHeader.length = *(uint32*)(((uint8*)installed_entry + 16)); + + size_dst += sawyercoding_write_chunk_buffer(dst_buffer+16, (uint8*)chunk, chunkHeader); + fwrite(dst_buffer, 1, size_dst, file); + + free(dst_buffer); +} + +/** +* +* rct2: 0x006AA2B7 +*/ +int object_load_packed(FILE *file) +{ + object_unload_all(); + + rct_object_entry* entry = RCT2_ADDRESS(0xF42B84, rct_object_entry); + + fread((void*)entry, 16, 1, file); + + uint8* chunk = rct2_malloc(0x600000); + uint32 chunkSize = sawyercoding_read_chunk(file, chunk); + chunk = rct2_realloc(chunk, chunkSize); + if (chunk == NULL){ + return 0; + } + + if (object_calculate_checksum(entry, chunk, chunkSize) != entry->checksum){ + rct2_free(chunk); + return 0; + } + + if (object_paint(entry->flags & 0x0F, 2, 0, entry->flags & 0x0F, 0, (int)chunk, 0, 0)) { + rct2_free(chunk); + return 0; + } + + int yyy = RCT2_GLOBAL(0x009ADAF0, uint32); + + if (yyy >= 0x4726E){ + rct2_free(chunk); + return 0; + } + + int type = entry->flags & 0x0F; + + // ecx + int entryGroupIndex = 0; + + for (; entryGroupIndex < object_entry_group_counts[type]; entryGroupIndex++){ + if (RCT2_ADDRESS(0x98D97C, uint32*)[type * 2][entryGroupIndex] == -1){ + break; + } + } + + if (entryGroupIndex == object_entry_group_counts[type]){ + rct2_free(chunk); + return 0; + } + + RCT2_ADDRESS(0x98D97C, uint8**)[type * 2][entryGroupIndex] = chunk; + int* edx = (int*)(entryGroupIndex * 20 + RCT2_ADDRESS(0x98D980, uint32)[type * 2]); + memcpy(edx, (int*)entry, 16); + *(edx + 4) = chunkSize; + + //esi + rct_object_entry *installedObject = RCT2_GLOBAL(RCT2_ADDRESS_INSTALLED_OBJECT_LIST, rct_object_entry*); + + if (RCT2_GLOBAL(0xF42B6C, uint32)){ + for (uint32 i = 0; i < RCT2_GLOBAL(0xF42B6C, uint32); ++i){ + if (object_entry_compare(entry, installedObject)){ + object_unload_all(); + return 0; + } + installedObject = object_get_next(installedObject); + } + } + + //Installing new data + //format_string(0x141ED68, 3163, 0); + //Code for updating progress bar removed. + + char path[260]; + char objectPath[13] = { 0 }; + for (int i = 0; i < 8; ++i){ + if (entry->name[i] != ' ') + objectPath[i] = toupper(entry->name[i]); + else + objectPath[i] = '\0'; + } + + subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), objectPath); + char* last_char = path + strlen(path); + strcat(path, ".DAT"); + + // + for (; osinterface_file_exists(path);){ + for (char* curr_char = last_char - 1;; --curr_char){ + if (*curr_char == '\\'){ + subsitute_path(path, RCT2_ADDRESS(RCT2_ADDRESS_OBJECT_DATA_PATH, char), "00000000.DAT"); + char* last_char = path + strlen(path); + break; + } + if (*curr_char < '0') *curr_char = '0'; + else if (*curr_char == '9') *curr_char = 'A'; + else if (*curr_char == 'Z') *curr_char = '0'; + else (*curr_char)++; + if (*curr_char != '0') break; + } + } + + // Removed progress bar code + + // The following section cannot be finished until 6A9F42 is finished + // Run the game once with vanila rct2 to not reach this part of code. + RCT2_ERROR("Function not finished. Please run this save once with vanila rct2."); + FILE* obj_file = fopen(path, "wb"); + if (obj_file){ + // Removed progress bar code + sub_6A9F42(obj_file, entry); + fclose(obj_file); + // Removed progress bar code + object_unload_all(); + // Removed progress bar code + return 1; + } + else{ + object_unload_all(); + return 0; + } + //create file + //6aa48C + int eax = 1;//, ebx = 0, ecx = 0, edx = 0, esi = 0, edi = 0, ebp = 0; + //RCT2_CALLFUNC_X(0x006AA2B7, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); + return eax; } /** @@ -42,7 +301,7 @@ void object_unload(int groupIndex, rct_object_entry_extended *entry) RCT2_CALLPROC_X(0x006A9CAF, 0, groupIndex, 0, 0, 0, 0, (int)entry); } -static int object_entry_compare(rct_object_entry *a, rct_object_entry *b) +int object_entry_compare(rct_object_entry *a, rct_object_entry *b) { if (a->flags & 0xF0) { if ((a->flags & 0x0F) != (b->flags & 0x0F)) @@ -65,7 +324,7 @@ static int object_entry_compare(rct_object_entry *a, rct_object_entry *b) return 1; } -static int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength) +int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength) { int i; char *eee = (char*)entry; @@ -114,7 +373,7 @@ int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, if (type == 10){ if (eax == 0) return object_scenario_load_custom_text((char*)esi); } - return RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098D9D4, uint32)[type], eax, ebx, ecx, edx, esi, edi, ebp) & 0x400; + return RCT2_CALLPROC_X(RCT2_ADDRESS(0x0098D9D4, uint32)[type], eax, ebx, ecx, edx, esi, edi, ebp) & 0x100; } /** @@ -215,7 +474,7 @@ rct_object_entry *object_get_next(rct_object_entry *entry) { char *pos = (char*)entry; - // Skip + // Skip sizeof(rct_object_entry) pos += 16; // Skip filename @@ -231,7 +490,7 @@ rct_object_entry *object_get_next(rct_object_entry *entry) pos++; } while (*(pos - 1) != 0); - // Skip + // Skip size of chunk pos += 4; // Skip @@ -244,4 +503,4 @@ rct_object_entry *object_get_next(rct_object_entry *entry) pos += 4; return (rct_object_entry*)pos; -} \ No newline at end of file +} diff --git a/src/object.h b/src/object.h index 04e14eb220..b0e125ae8d 100644 --- a/src/object.h +++ b/src/object.h @@ -45,9 +45,12 @@ typedef struct { uint32 extended; } rct_object_entry_extended; +extern int object_entry_group_counts[]; +extern int object_entry_group_encoding[]; + void object_list_load(); int object_read_and_load_entries(FILE *file); -int object_load_packed(); +int object_load_packed(FILE *file); void object_unload_all(); int object_load(int groupIndex, rct_object_entry *entry); @@ -56,5 +59,7 @@ int object_get_scenario_text(rct_object_entry *entry); void object_free_scenario_text(); int object_get_length(rct_object_entry *entry); rct_object_entry *object_get_next(rct_object_entry *entry); +int object_calculate_checksum(rct_object_entry *entry, char *data, int dataLength); +int object_paint(int type, int eax, int ebx, int ecx, int edx, int esi, int edi, int ebp); #endif diff --git a/src/object_list.c b/src/object_list.c index 9765363808..39f8a996cd 100644 --- a/src/object_list.c +++ b/src/object_list.c @@ -34,6 +34,7 @@ typedef struct { uint32 var_10; } rct_plugin_header; +// 98DA00 int object_entry_group_counts[] = { 128, // rides 252, // small scenery @@ -48,6 +49,21 @@ int object_entry_group_counts[] = { 1 // scenario text }; +// 98DA2C +int object_entry_group_encoding[] = { + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_RLE, + CHUNK_ENCODING_ROTATE +}; + struct { void **data; rct_object_entry_extended *entries; } object_entry_groups[] = { (void**)(0x009ACFA4 ), (rct_object_entry_extended*)(0x00F3F03C ), // rides (void**)(0x009ACFA4 + (128 * 4)), (rct_object_entry_extended*)(0x00F3F03C + (128 * 20)), // small scenery @@ -184,9 +200,12 @@ int object_read_and_load_entries(FILE *file) // Load the obect if (!object_load(entryGroupIndex, &entries[i])) { // Failed to load the object - free(entries); + //Destroy progress bar + memcpy((char*)0x13CE952, entries[i].name, 8); + free(entries); object_unload_all(); + RCT2_GLOBAL(0x14241BC, uint32) = 0; return 0; } } @@ -195,16 +214,7 @@ int object_read_and_load_entries(FILE *file) return 1; } -/** - * - * rct2: 0x006AA2B7 - */ -int object_load_packed() -{ - int eax, ebx, ecx, edx, esi, edi, ebp; - RCT2_CALLFUNC_X(0x006AA2B7, &eax, &ebx, &ecx, &edx, &esi, &edi, &ebp); - return eax; -} + /** * @@ -218,4 +228,4 @@ void object_unload_all() for (j = 0; j < object_entry_group_counts[i]; j++) if (object_entry_groups[i].data[j] != (void**)0xFFFFFFFF) object_unload(j, &object_entry_groups[i].entries[j]); -} \ No newline at end of file +} diff --git a/src/sawyercoding.c b/src/sawyercoding.c index 10af63d500..c62722f8b7 100644 --- a/src/sawyercoding.c +++ b/src/sawyercoding.c @@ -24,10 +24,26 @@ #include "rct2.h" #include "sawyercoding.h" -static int decode_chunk_rle(char *buffer, int length); +static int decode_chunk_rle(uint8* src_buffer, uint8* dst_buffer, int length); static int decode_chunk_repeat(char *buffer, int length); static void decode_chunk_rotate(char *buffer, int length); +int encode_chunk_rle(char *src_buffer, char *dst_buffer, int length); +void encode_chunk_rotate(char *buffer, int length); + +int sawyercoding_calculate_checksum(uint8* buffer, uint32 length){ + int checksum = 0; + do { + int bufferSize = min(length , 1024); + + for (int i = 0; i < bufferSize; i++) + checksum += buffer[i]; + length -= bufferSize; + } while (length != 0); + + return checksum; +} + /** * * rct2: 0x00676FD2 @@ -68,6 +84,7 @@ int sawyercoding_validate_checksum(FILE *file) return checksum == fileChecksum; } + /** * * rct2: 0x0067685F @@ -83,26 +100,33 @@ int sawyercoding_read_chunk(FILE *file, uint8 *buffer) return -1; } + uint8* src_buffer = malloc(chunkHeader.length); + // Read chunk data - if (fread(buffer, chunkHeader.length, 1, file) != 1) { + if (fread(src_buffer, chunkHeader.length, 1, file) != 1) { + free(src_buffer); RCT2_ERROR("Unable to read chunk data!"); return -1; } // Decode chunk data switch (chunkHeader.encoding) { + case CHUNK_ENCODING_NONE: + memcpy(buffer, src_buffer, chunkHeader.length); + break; case CHUNK_ENCODING_RLE: - chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length); + chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length); break; case CHUNK_ENCODING_RLECOMPRESSED: - chunkHeader.length = decode_chunk_rle(buffer, chunkHeader.length); + chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length); chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length); break; case CHUNK_ENCODING_ROTATE: + memcpy(buffer, src_buffer, chunkHeader.length); decode_chunk_rotate(buffer, chunkHeader.length); break; } - + free(src_buffer); // Set length RCT2_GLOBAL(0x009E3828, uint32) = chunkHeader.length; return chunkHeader.length; @@ -112,34 +136,28 @@ int sawyercoding_read_chunk(FILE *file, uint8 *buffer) * * rct2: 0x0067693A */ -static int decode_chunk_rle(char *buffer, int length) +static int decode_chunk_rle(uint8* src_buffer, uint8* dst_buffer, int length) { int i, j, count; - unsigned char *src, *dst, rleCodeByte; + uint8 *dst, rleCodeByte; - // Backup buffer - src = malloc(length); - memcpy(src, buffer, length); - dst = buffer; + dst = dst_buffer; for (i = 0; i < length; i++) { - rleCodeByte = src[i]; + rleCodeByte = src_buffer[i]; if (rleCodeByte & 128) { i++; count = 257 - rleCodeByte; for (j = 0; j < count; j++) - *dst++ = src[i]; + *dst++ = src_buffer[i]; } else { for (j = 0; j <= rleCodeByte; j++) - *dst++ = src[++i]; + *dst++ = src_buffer[++i]; } } - // Free backup buffer - free(src); - // Return final size - return (char*)dst - buffer; + return dst - dst_buffer; } /** @@ -186,3 +204,103 @@ static void decode_chunk_rotate(char *buffer, int length) code = (code + 2) % 8; } } + +/** +* +* rct2: 0x006762E1 +* +*/ +int sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding_chunk_header chunkHeader){ + uint8* encode_buffer; + + switch (chunkHeader.encoding){ + case CHUNK_ENCODING_NONE: + memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header)); + dst_file += sizeof(sawyercoding_chunk_header); + memcpy(dst_file, buffer, chunkHeader.length); + //fwrite(&chunkHeader, sizeof(sawyercoding_chunk_header), 1, file); + //fwrite(buffer, 1, chunkHeader.length, file); + break; + case CHUNK_ENCODING_RLE: + encode_buffer = malloc(0x600000); + chunkHeader.length = encode_chunk_rle(buffer, encode_buffer, chunkHeader.length); + memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header)); + dst_file += sizeof(sawyercoding_chunk_header); + memcpy(dst_file, encode_buffer, chunkHeader.length); + + free(encode_buffer); + break; + case CHUNK_ENCODING_RLECOMPRESSED: + RCT2_ERROR("This has not been implemented"); + return -1; + //chunkHeader.length = decode_chunk_rle(src_buffer, buffer, chunkHeader.length); + //chunkHeader.length = decode_chunk_repeat(buffer, chunkHeader.length); + break; + case CHUNK_ENCODING_ROTATE: + encode_chunk_rotate(buffer, chunkHeader.length); + memcpy(dst_file, &chunkHeader, sizeof(sawyercoding_chunk_header)); + dst_file += sizeof(sawyercoding_chunk_header); + memcpy(dst_file, buffer, chunkHeader.length); + break; + } + + return chunkHeader.length + sizeof(sawyercoding_chunk_header); +} + +/** +* Ensure dst_buffer is bigger than src_buffer then resize afterwards +* returns length of dst_buffer +*/ +int encode_chunk_rle(char *src_buffer, char *dst_buffer, int length) +{ + char* src = src_buffer; + char* dst = dst_buffer; + char* end_src = src + length; + uint8 count = 0; + char* src_norm_start = src; + + while (src < end_src - 1){ + + if ((count && *src == src[1]) || count > 120){ + *dst++ = count - 1; + for (; count != 0; --count){ + *dst++ = *src_norm_start++; + } + } + if (*src == src[1]){ + for (; (count < 120) && ((src + count) < end_src); count++){ + if (*src != src[count]) break; + } + *dst++ = 257 - count; + *dst++ = *src; + src += count; + src_norm_start = src; + count = 0; + } + else{ + count++; + src++; + } + } + if (src == end_src - 1)count++; + if (count){ + *dst++ = count - 1; + for (; count != 0; --count){ + *dst++ = *src_norm_start++; + } + } + return dst - dst_buffer; +} + +/** +* +* +*/ +void encode_chunk_rotate(char *buffer, int length) +{ + int i, code = 1; + for (i = 0; i < length; i++) { + buffer[i] = rol8(buffer[i], code); + code = (code + 2) % 8; + } +} diff --git a/src/sawyercoding.h b/src/sawyercoding.h index 38916cf1c3..c965d2da94 100644 --- a/src/sawyercoding.h +++ b/src/sawyercoding.h @@ -37,6 +37,8 @@ enum { }; int sawyercoding_validate_checksum(FILE *file); +int sawyercoding_calculate_checksum(uint8* buffer, uint32 length); int sawyercoding_read_chunk(FILE *file, uint8 *buffer); +int sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding_chunk_header chunkHeader); #endif diff --git a/src/scenario.c b/src/scenario.c index 8e58d09e78..b27fd4f830 100644 --- a/src/scenario.c +++ b/src/scenario.c @@ -109,7 +109,7 @@ void scenario_load(const char *path) if (s6Header->num_packed_objects > 0) { j = 0; for (i = 0; i < s6Header->num_packed_objects; i++) - j += object_load_packed(); + j += object_load_packed(file); if (j > 0) object_list_load(); }