bool sprite_file_save(const char *path) { FILE *file = fopen(path, "wb"); if (file == NULL) return false; if (fwrite(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1) { fclose(file); return false; } if (spriteFileHeader.num_entries > 0) { sprite_entries_make_relative(); int entryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element); if (fwrite(spriteFileEntries, entryTableSize, 1, file) != 1) { sprite_entries_make_absolute(); fclose(file); return false; } else { sprite_entries_make_absolute(); } if (fwrite(spriteFileData, spriteFileHeader.total_size, 1, file) != 1) { fclose(file); return false; } } fclose(file); return true; }
bool sprite_file_save(const char *path) { SDL_RWops *file = SDL_RWFromFile(path, "wb"); if (file == NULL) return false; if (SDL_RWwrite(file, &spriteFileHeader, sizeof(rct_sprite_file_header), 1) != 1) { SDL_RWclose(file); return false; } if (spriteFileHeader.num_entries > 0) { sprite_entries_make_relative(); int entryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element); if (SDL_RWwrite(file, spriteFileEntries, entryTableSize, 1) != 1) { sprite_entries_make_absolute(); SDL_RWclose(file); return false; } else { sprite_entries_make_absolute(); } if (SDL_RWwrite(file, spriteFileData, spriteFileHeader.total_size, 1) != 1) { SDL_RWclose(file); return false; } } SDL_RWclose(file); return true; }
bool sprite_file_open(const char *path) { FILE *file; file = fopen(path, "rb"); if (file == NULL) return false; if (fread(&spriteFileHeader, sizeof(rct_sprite_file_header), 1, file) != 1) { fclose(file); return false; } if (spriteFileHeader.num_entries > 0) { int entryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element); spriteFileEntries = malloc(entryTableSize); if (fread(spriteFileEntries, entryTableSize, 1, file) != 1) { fclose(file); return false; } spriteFileData = malloc(spriteFileHeader.total_size); if (fread(spriteFileData, spriteFileHeader.total_size, 1, file) != 1) { fclose(file); return false; } sprite_entries_make_absolute(); } fclose(file); return true; }
bool sprite_file_open(const utf8 *path) { SDL_RWops *file; file = SDL_RWFromFile(path, "rb"); if (file == NULL) return false; if (SDL_RWread(file, &spriteFileHeader, sizeof(rct_sprite_file_header), 1) != 1) { SDL_RWclose(file); return false; } if (spriteFileHeader.num_entries > 0) { int entryTableSize = spriteFileHeader.num_entries * sizeof(rct_g1_element); spriteFileEntries = malloc(entryTableSize); if (SDL_RWread(file, spriteFileEntries, entryTableSize, 1) != 1) { SDL_RWclose(file); return false; } spriteFileData = malloc(spriteFileHeader.total_size); if (SDL_RWread(file, spriteFileData, spriteFileHeader.total_size, 1) != 1) { SDL_RWclose(file); return false; } sprite_entries_make_absolute(); } SDL_RWclose(file); return true; }
int cmdline_for_sprite(const char **argv, int argc) { if (argc == 0) return -1; if (_strcmpi(argv[0], "details") == 0) { if (argc < 2) { fprintf(stderr, "usage: sprite details <spritefile> [idx]\n"); return -1; } else if (argc == 2) { const char *spriteFilePath = argv[1]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } printf("sprites: %d\n", spriteFileHeader.num_entries); printf("data size: %d\n", spriteFileHeader.total_size); sprite_file_close(); return 1; } else { const char *spriteFilePath = argv[1]; int spriteIndex = atoi(argv[2]); if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } if (spriteIndex < 0 || spriteIndex >= (int)spriteFileHeader.num_entries) { sprite_file_close(); fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); return -1; } rct_g1_element *g1 = &spriteFileEntries[spriteIndex]; printf("width: %d\n", g1->width); printf("height: %d\n", g1->height); printf("x offset: %d\n", g1->x_offset); printf("y offset: %d\n", g1->y_offset); printf("data offset: 0x%X\n", g1->offset); sprite_file_close(); return 1; } } else if (_strcmpi(argv[0], "export") == 0) { if (argc < 4) { fprintf(stderr, "usage: sprite export <spritefile> <idx> <output>\n"); return -1; } const char *spriteFilePath = argv[1]; int spriteIndex = atoi(argv[2]); const char *outputPath = argv[3]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } if (spriteIndex < 0 || spriteIndex >= (int)spriteFileHeader.num_entries) { fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); return -1; } if (!sprite_file_export(spriteIndex, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); return -1; } sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "exportall") == 0) { if (argc < 3) { fprintf(stderr, "usage: sprite exportall <spritefile> <output directory>\n"); return -1; } const char *spriteFilePath = argv[1]; char outputPath[_MAX_PATH]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } if (!platform_ensure_directory_exists(argv[2])){ fprintf(stderr, "Unable to create directory.\n"); return -1; } int maxIndex = (int)spriteFileHeader.num_entries; int numbers = (int)floor(log(maxIndex)); strncpy(outputPath, argv[2], _MAX_PATH); int pathLen = strlen(outputPath); if (pathLen >= _MAX_PATH - numbers - 5){ fprintf(stderr, "Path too long.\n"); return -1; } for (int x = 0; x < numbers; x++){ outputPath[pathLen + x] = '0'; } strncpy(outputPath + pathLen + numbers, ".png", _MAX_PATH); for (int spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++){ if (spriteIndex % 100 == 99){ // Status indicator printf("\r%d / %d, %d%%", spriteIndex, maxIndex, spriteIndex / maxIndex); } // Add to the index at the end of the file name char *counter = outputPath + pathLen + numbers - 1; (*counter)++; while (*counter > '9'){ *counter = '0'; counter--; (*counter)++; } if (!sprite_file_export(spriteIndex, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); return -1; } } sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "create") == 0) { if (argc < 2) { fprintf(stderr, "usage: sprite create <spritefile>\n"); return -1; } const char *spriteFilePath = argv[1]; spriteFileHeader.num_entries = 0; spriteFileHeader.total_size = 0; sprite_file_save(spriteFilePath); sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "append") == 0) { if (argc < 3) { fprintf(stderr, "usage: sprite append <spritefile> <input>\n"); return -1; } const char *spriteFilePath = argv[1]; const char *imagePath = argv[2]; rct_g1_element spriteElement; uint8 *buffer; int bufferLength; if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength, sprite_mode)) return -1; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } spriteFileHeader.num_entries++; spriteFileHeader.total_size += bufferLength; spriteFileEntries = realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)); sprite_entries_make_relative(); spriteFileData = realloc(spriteFileData, spriteFileHeader.total_size); sprite_entries_make_absolute(); spriteFileEntries[spriteFileHeader.num_entries - 1] = spriteElement; memcpy(spriteFileData + (spriteFileHeader.total_size - bufferLength), buffer, bufferLength); spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData + (spriteFileHeader.total_size - bufferLength); free(buffer); if (!sprite_file_save(spriteFilePath)) return -1; return 1; } else if (_strcmpi(argv[0], "build") == 0) { if (argc < 3) { fprintf(stderr, "usage: sprite build <spritefile> <resourcedir> [silent]\n"); return -1; } const char *spriteFilePath = argv[1]; const char *resourcePath = argv[2]; char imagePath[MAX_PATH]; int resourceLength = strlen(resourcePath); bool silent = (argc >= 4 && strcmp(argv[3], "silent") == 0); bool fileExists = true; FILE *file; spriteFileHeader.num_entries = 0; spriteFileHeader.total_size = 0; sprite_file_save(spriteFilePath); fprintf(stderr, "Building: %s\n", spriteFilePath); int i = 0; do { // Create image path strcpy(imagePath, resourcePath); if (resourcePath[resourceLength - 1] == '/' || resourcePath[resourceLength - 1] == '\\') imagePath[resourceLength - 1] = 0; sprintf(imagePath, "%s%c%d.png", imagePath, platform_get_path_separator(), i); file = fopen(imagePath, "r"); if (file != NULL) { fclose(file); rct_g1_element spriteElement; uint8 *buffer; int bufferLength; if (!sprite_file_import(imagePath, &spriteElement, &buffer, &bufferLength, sprite_mode)) { fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath); return -1; } if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath); return -1; } spriteFileHeader.num_entries++; spriteFileHeader.total_size += bufferLength; spriteFileEntries = realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)); sprite_entries_make_relative(); spriteFileData = realloc(spriteFileData, spriteFileHeader.total_size); sprite_entries_make_absolute(); spriteFileEntries[spriteFileHeader.num_entries - 1] = spriteElement; memcpy(spriteFileData + (spriteFileHeader.total_size - bufferLength), buffer, bufferLength); spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData + (spriteFileHeader.total_size - bufferLength); free(buffer); if (!sprite_file_save(spriteFilePath)) { fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath); return -1; } if (!silent) fprintf(stderr, "Added: %s\n", imagePath); } i++; } while (file != NULL); fprintf(stderr, "Finished\n", imagePath); return 1; } else { fprintf(stderr, "Unknown sprite command."); return 1; } }
sint32 cmdline_for_sprite(const char **argv, sint32 argc) { gOpenRCT2Headless = true; if (argc == 0) return -1; if (_strcmpi(argv[0], "details") == 0) { if (argc < 2) { fprintf(stdout, "usage: sprite details <spritefile> [idx]\n"); return -1; } else if (argc == 2) { const char *spriteFilePath = argv[1]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } printf("sprites: %u\n", spriteFileHeader.num_entries); printf("data size: %u\n", spriteFileHeader.total_size); sprite_file_close(); return 1; } else { const char *spriteFilePath = argv[1]; sint32 spriteIndex = atoi(argv[2]); if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } if (spriteIndex < 0 || spriteIndex >= (sint32)spriteFileHeader.num_entries) { sprite_file_close(); fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); return -1; } rct_g1_element *g1 = &spriteFileEntries[spriteIndex]; printf("width: %d\n", g1->width); printf("height: %d\n", g1->height); printf("x offset: %d\n", g1->x_offset); printf("y offset: %d\n", g1->y_offset); printf("data offset: %p\n", g1->offset); sprite_file_close(); return 1; } } else if (_strcmpi(argv[0], "export") == 0) { if (argc < 4) { fprintf(stdout, "usage: sprite export <spritefile> <idx> <output>\n"); return -1; } const char *spriteFilePath = argv[1]; sint32 spriteIndex = atoi(argv[2]); const char *outputPath = argv[3]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } if (spriteIndex < 0 || spriteIndex >= (sint32)spriteFileHeader.num_entries) { fprintf(stderr, "Sprite #%d does not exist in sprite file.\n", spriteIndex); return -1; } if (!sprite_file_export(spriteIndex, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); return -1; } sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "exportall") == 0) { if (argc < 3) { fprintf(stdout, "usage: sprite exportall <spritefile> <output directory>\n"); return -1; } const char *spriteFilePath = argv[1]; char outputPath[MAX_PATH]; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } safe_strcpy(outputPath, argv[2], MAX_PATH); path_end_with_separator(outputPath, MAX_PATH); if (!platform_ensure_directory_exists(outputPath)){ fprintf(stderr, "Unable to create directory.\n"); return -1; } sint32 maxIndex = (sint32)spriteFileHeader.num_entries; sint32 numbers = (sint32)floor(log(maxIndex)); size_t pathLen = strlen(outputPath); if (pathLen >= (size_t)(MAX_PATH - numbers - 5)) { fprintf(stderr, "Path too long.\n"); return -1; } for (sint32 x = 0; x < numbers; x++){ outputPath[pathLen + x] = '0'; } safe_strcpy(outputPath + pathLen + numbers, ".png", MAX_PATH - pathLen - numbers); for (sint32 spriteIndex = 0; spriteIndex < maxIndex; spriteIndex++){ if (spriteIndex % 100 == 99){ // Status indicator printf("\r%d / %d, %d%%", spriteIndex, maxIndex, spriteIndex / maxIndex); } // Add to the index at the end of the file name char *counter = outputPath + pathLen + numbers - 1; (*counter)++; while (*counter > '9'){ *counter = '0'; counter--; (*counter)++; } if (!sprite_file_export(spriteIndex, outputPath)) { fprintf(stderr, "Could not export\n"); sprite_file_close(); return -1; } } sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "create") == 0) { if (argc < 2) { fprintf(stderr, "usage: sprite create <spritefile>\n"); return -1; } const char *spriteFilePath = argv[1]; spriteFileHeader.num_entries = 0; spriteFileHeader.total_size = 0; sprite_file_save(spriteFilePath); sprite_file_close(); return 1; } else if (_strcmpi(argv[0], "append") == 0) { if (argc != 3 && argc != 5) { fprintf(stderr, "usage: sprite append <spritefile> <input> [<x offset> <y offset>]\n"); return -1; } const char *spriteFilePath = argv[1]; const char *imagePath = argv[2]; sint16 x_offset = 0; sint16 y_offset = 0; if (argc == 5) { char *endptr; x_offset = strtol(argv[3], &endptr, 0); if (*endptr != 0) { fprintf(stderr, "X offset must be an integer\n"); return -1; } y_offset = strtol(argv[4], &endptr, 0); if (*endptr != 0) { fprintf(stderr, "Y offset must be an integer\n"); return -1; } } rct_g1_element spriteElement; uint8 *buffer; sint32 bufferLength; if (!sprite_file_import(imagePath, x_offset, y_offset, &spriteElement, &buffer, &bufferLength, gSpriteMode)) return -1; if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open input sprite file.\n"); return -1; } spriteFileHeader.num_entries++; spriteFileHeader.total_size += bufferLength; spriteFileEntries = realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)); sprite_entries_make_relative(); spriteFileData = realloc(spriteFileData, spriteFileHeader.total_size); sprite_entries_make_absolute(); spriteFileEntries[spriteFileHeader.num_entries - 1] = spriteElement; memcpy(spriteFileData + (spriteFileHeader.total_size - bufferLength), buffer, bufferLength); spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData + (spriteFileHeader.total_size - bufferLength); free(buffer); if (!sprite_file_save(spriteFilePath)) return -1; return 1; } else if (_strcmpi(argv[0], "build") == 0) { if (argc < 3) { fprintf(stdout, "usage: sprite build <spritefile> <sprite description file> [silent]\n"); return -1; } const char *spriteFilePath = argv[1]; const char *spriteDescriptionPath = argv[2]; char* directoryPath = path_get_directory(spriteDescriptionPath); json_error_t error; json_t* sprite_list=json_load_file(spriteDescriptionPath, JSON_REJECT_DUPLICATES, &error); if (sprite_list == NULL) { fprintf(stderr, "Error parsing sprite description file: %s at line %d column %d\n", error.text, error.line, error.column); return -1; } if (!json_is_array(sprite_list)) { fprintf(stderr, "Error: expected array\n"); json_decref(sprite_list); return -1; } bool silent = (argc >= 4 && strcmp(argv[3], "silent") == 0); spriteFileHeader.num_entries = 0; spriteFileHeader.total_size = 0; sprite_file_save(spriteFilePath); fprintf(stdout, "Building: %s\n", spriteFilePath); size_t i; json_t* sprite_description; json_array_foreach(sprite_list, i, sprite_description) { if(!json_is_object(sprite_description)) { fprintf(stderr, "Error: expected object for sprite %lu\n", (unsigned long)i); json_decref(sprite_list); return -1; } json_t* path = json_object_get(sprite_description,"path"); if(!path || !json_is_string(path)) { fprintf(stderr, "Error: no path provided for sprite %lu\n", (unsigned long)i); json_decref(sprite_list); return -1; } // Get x and y offsets, if present json_t* x_offset = json_object_get(sprite_description, "x_offset"); json_t* y_offset = json_object_get(sprite_description, "y_offset"); // Resolve absolute sprite path char *imagePath = platform_get_absolute_path(json_string_value(path), directoryPath); rct_g1_element spriteElement; uint8 *buffer; int bufferLength; if (!sprite_file_import(imagePath, x_offset==NULL ? 0 : json_integer_value(x_offset), y_offset==NULL ? 0 : json_integer_value(y_offset), &spriteElement, &buffer, &bufferLength, gSpriteMode)) { fprintf(stderr, "Could not import image file: %s\nCanceling\n", imagePath); json_decref(sprite_list); free(imagePath); return -1; } if (!sprite_file_open(spriteFilePath)) { fprintf(stderr, "Unable to open sprite file: %s\nCanceling\n", spriteFilePath); json_decref(sprite_list); free(imagePath); return -1; } spriteFileHeader.num_entries++; spriteFileHeader.total_size += bufferLength; spriteFileEntries = realloc(spriteFileEntries, spriteFileHeader.num_entries * sizeof(rct_g1_element)); sprite_entries_make_relative(); spriteFileData = realloc(spriteFileData, spriteFileHeader.total_size); sprite_entries_make_absolute(); spriteFileEntries[spriteFileHeader.num_entries - 1] = spriteElement; memcpy(spriteFileData + (spriteFileHeader.total_size - bufferLength), buffer, bufferLength); spriteFileEntries[spriteFileHeader.num_entries - 1].offset = spriteFileData + (spriteFileHeader.total_size - bufferLength); free(buffer); if (!sprite_file_save(spriteFilePath)) { fprintf(stderr, "Could not save sprite file: %s\nCanceling\n", imagePath); json_decref(sprite_list); free(imagePath); return -1; } if (!silent) fprintf(stdout, "Added: %s\n", imagePath); free(imagePath); sprite_file_close(); } json_decref(sprite_list); free(directoryPath); fprintf(stdout, "Finished\n"); return 1; } else {