1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 22:34:33 +01:00

implement saving of games and scenarios

This commit is contained in:
IntelOrca
2014-11-26 00:51:26 +00:00
parent e6fc8cacf8
commit 4b897ae1a3
6 changed files with 286 additions and 16 deletions

View File

@@ -619,9 +619,13 @@ int game_load_save(const char *path)
FILE *file;
int i, j;
log_verbose("loading saved game, %s", path);
strcpy((char*)0x0141EF68, path);
file = fopen(path, "rb");
if (file == NULL) {
log_error("unable to open %s", path);
RCT2_GLOBAL(0x009AC31B, uint8) = 255;
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, uint16) = STR_FILE_CONTAINS_INVALID_DATA;
return 0;
@@ -629,6 +633,9 @@ int game_load_save(const char *path)
if (!sawyercoding_validate_checksum(file)) {
fclose(file);
log_error("invalid checksum, %s", path);
RCT2_GLOBAL(0x009AC31B, uint8) = 255;
RCT2_GLOBAL(RCT2_ADDRESS_GAME_COMMAND_ERROR_STRING_ID, uint16) = STR_FILE_CONTAINS_INVALID_DATA;
return 0;

View File

@@ -61,5 +61,6 @@ 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);
int sub_6A9F42(FILE *file, rct_object_entry* entry);
#endif

View File

@@ -33,6 +33,7 @@
#include "ride/ride.h"
#include "scenario.h"
#include "util/sawyercoding.h"
#include "util/util.h"
#include "world/map.h"
#include "world/park.h"
#include "world/sprite.h"
@@ -740,6 +741,87 @@ int scenario_prepare_for_save()
return 1;
}
/**
*
* rct2: 0x006AA244
*/
int scenario_get_num_packed_objects_to_write()
{
int i, count = 0;
rct_object_entry_extended *entry = (rct_object_entry_extended*)0x00F3F03C;
for (i = 0; i < 721; i++, entry++) {
if (RCT2_ADDRESS(0x009ACFA4, uint32)[i] == 0xFFFFFFFF || (entry->flags & 0xF0))
continue;
count++;
}
return count;
}
/**
*
* rct2: 0x006AA26E
*/
int scenario_write_packed_objects(FILE *file)
{
int i;
rct_object_entry_extended *entry = (rct_object_entry_extended*)0x00F3F03C;
for (i = 0; i < 721; i++, entry++) {
if (RCT2_ADDRESS(0x009ACFA4, uint32)[i] == 0xFFFFFFFF || (entry->flags & 0xF0))
continue;
if (!sub_6A9F42(file, (rct_object_entry*)entry))
return 0;
}
return 1;
}
/**
*
* rct2: 0x006AA039
*/
int scenario_write_available_objects(FILE *file)
{
char *buffer, *dstBuffer;
int i, encodedLength;
sawyercoding_chunk_header chunkHeader;
const int totalEntries = 721;
const int bufferLength = totalEntries * sizeof(rct_object_entry);
// Initialise buffers
buffer = malloc(bufferLength);
dstBuffer = malloc(bufferLength + sizeof(sawyercoding_chunk_header));
if (buffer == NULL || dstBuffer == NULL)
return 0;
// Write entries
rct_object_entry_extended *srcEntry = (rct_object_entry_extended*)0x00F3F03C;
rct_object_entry *dstEntry = (rct_object_entry*)buffer;
for (i = 0; i < 721; i++) {
if (RCT2_ADDRESS(0x009ACFA4, uint32)[i] == 0xFFFFFFFF)
memset(dstEntry, 0xFF, sizeof(rct_object_entry));
else
*dstEntry = *((rct_object_entry*)srcEntry);
srcEntry++;
dstEntry++;
}
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = bufferLength;
encodedLength = sawyercoding_write_chunk_buffer(dstBuffer, buffer, chunkHeader);
fwrite(dstBuffer, encodedLength, 1, file);
// Free buffers
free(dstBuffer);
free(buffer);
}
/**
*
* rct2: 0x006754F5
@@ -747,11 +829,193 @@ int scenario_prepare_for_save()
*/
int scenario_save(char *path, int flags)
{
rct_s6_header *s6Header = (rct_s6_header*)0x009E34E4;
rct_s6_info *s6Info = (rct_s6_info*)0x0141F570;
FILE *file;
char *buffer;
sawyercoding_chunk_header chunkHeader;
int encodedLength;
long fileSize;
uint32 checksum;
rct_window *w;
rct_viewport *viewport;
int viewX, viewY, viewZoom, viewRotation;
if (flags & 2)
log_verbose("saving scenario, %s", path);
else
log_verbose("saving game, %s", path);
strcpy((char*)0x0141EF68, path);
return !(RCT2_CALLPROC_X(0x006754F5, flags, 0, 0, 0, 0, 0, 0) & 0x100);
if (!(flags & 0x80000000))
window_close_construction_windows();
RCT2_CALLPROC_EBPSAFE(0x0068B111);
RCT2_CALLPROC_EBPSAFE(0x0069EBE4);
RCT2_CALLPROC_EBPSAFE(0x0069EBA4);
RCT2_CALLPROC_EBPSAFE(0x00677552);
RCT2_CALLPROC_EBPSAFE(0x00674BCF);
// Set saved view
w = window_get_main();
if (w != NULL) {
viewport = w->viewport;
viewX = viewport->view_width / 2 + viewport->view_x;
viewY = viewport->view_height / 2 + viewport->view_y;
viewZoom = viewport->zoom;
viewRotation = RCT2_GLOBAL(RCT2_ADDRESS_CURRENT_ROTATION, uint8);
} else {
viewX = 0;
viewY = 0;
viewZoom = 0;
viewRotation = 0;
}
RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_X, uint16) = viewX;
RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_Y, uint16) = viewY;
RCT2_GLOBAL(RCT2_ADDRESS_SAVED_VIEW_ZOOM_AND_ROTATION, uint16) = viewZoom | (viewRotation << 8);
//
memset(s6Header, 0, sizeof(rct_s6_header));
s6Header->type = flags & 2 ? S6_TYPE_SCENARIO : S6_TYPE_SAVEDGAME;
s6Header->num_packed_objects = flags & 1 ? scenario_get_num_packed_objects_to_write() : 0;
s6Header->version = S6_RCT2_VERSION;
s6Header->magic_number = S6_MAGIC_NUMBER;
file = fopen(path, "wb");
if (file == NULL) {
log_error("Unable to write to %s", path);
return 0;
}
buffer = malloc(0x600000);
if (buffer == NULL) {
log_error("Unable to allocate enough space for a write buffer.");
fclose(file);
return 0;
}
// Write header chunk
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = sizeof(rct_s6_header);
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)s6Header, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write scenario info chunk
if (flags & 2) {
chunkHeader.encoding = CHUNK_ENCODING_ROTATE;
chunkHeader.length = sizeof(rct_s6_info);
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)s6Info, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
}
// Write packed objects
if (s6Header->num_packed_objects > 0) {
if (!scenario_write_packed_objects(file)) {
free(buffer);
fclose(file);
return 0;
}
}
// Write available objects chunk
scenario_write_available_objects(file);
// Write date etc. chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 16;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x00F663A8, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write map elements
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x4A85EC;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x00F663B8, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
if (flags & 2) {
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x27104C;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x010E63B8, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 4;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x01357844, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 8;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x01357BC8, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 2;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x01357CB0, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 1082;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x01357CF2, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 16;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x0135832C, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 4;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x0135853C, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x761E8;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x01358740, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
} else {
// Write chunk
chunkHeader.encoding = CHUNK_ENCODING_RLECOMPRESSED;
chunkHeader.length = 0x2E8570;
encodedLength = sawyercoding_write_chunk_buffer(buffer, (uint8*)0x010E63B8, chunkHeader);
fwrite(buffer, encodedLength, 1, file);
}
free(buffer);
// Due to the buffered writing and the writing of packed objects, there is no single buffer to calculate the checksum.
// Therefore the file is closed, opened, closed and opened again to save the checksum. Inefficient, but will do until
// the chunk writing code is better.
fclose(file);
file = fopen(path, "rb");
fileSize = fsize(file);
buffer = malloc(fileSize);
fread(buffer, fileSize, 1, file);
fclose(file);
checksum = sawyercoding_calculate_checksum(buffer, fileSize);
fopen(path, "wb");
fwrite(buffer, fileSize, 1, file);
fwrite(&checksum, sizeof(int), 1, file);
fclose(file);
if (!(flags & 0x80000000))
RCT2_CALLPROC_EBPSAFE(0x006A9FC0);
gfx_invalidate_screen();
RCT2_GLOBAL(0x009DEA66, uint16) = 0;
return 1;
}

View File

@@ -372,6 +372,9 @@ enum {
S6_TYPE_SCENARIO
};
#define S6_RCT2_VERSION 120001
#define S6_MAGIC_NUMBER 0x00031144
enum {
SCENARIO_CATEGORY_BEGINNER,
SCENARIO_CATEGORY_CHALLENGING,

View File

@@ -30,15 +30,11 @@ 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);
uint32 sawyercoding_calculate_checksum(uint8* buffer, uint32 length)
{
uint32 i, checksum = 0;
for (i = 0; i < length; i++)
checksum += buffer[i];
return checksum;
}
@@ -230,10 +226,9 @@ int sawyercoding_write_chunk_buffer(uint8 *dst_file, uint8* buffer, sawyercoding
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);
log_warning("RLECOMPRESSED encoding has not been implemented, using CHUNK_ENCODING_RLE instead.");
chunkHeader.encoding = CHUNK_ENCODING_RLE;
return sawyercoding_write_chunk_buffer(dst_file, buffer, chunkHeader);
break;
case CHUNK_ENCODING_ROTATE:
encode_chunk_rotate(buffer, chunkHeader.length);

View File

@@ -37,7 +37,7 @@ enum {
};
int sawyercoding_validate_checksum(FILE *file);
int sawyercoding_calculate_checksum(uint8* buffer, uint32 length);
uint32 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);
int sawyercoding_decode_sv4(char *src, char *dst, int length);