Add -split_into_oam_shapes parameter

This commit is contained in:
JaceCear committed 2023-03-03 18:38:49 +01:00
1 parent 358bbf0555
commit 924e32eb63
5 files changed
+204 -22

No files matched your search

+188 -15
View File
@@ -4,6 +4,8 @@
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h> // memcpy
#include <malloc.h> // memcpy
#include "global.h"
#include "gfx.h"
#include "util.h"
@@ -245,39 +247,152 @@ void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int
free(buffer);
}
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool ignoreEmptyTrailingTiles)
{
int tileSize = bitDepth * 8;
struct OamShape;
struct OamShape {
struct OamShape *next;
short offX;
short offY;
unsigned char width;
unsigned char height;
};
struct OamShapes {
size_t count;
struct OamShape next[1];
};
typedef struct {
unsigned char width;
unsigned char height;
} OamDim;
OamDim oamSizes[12] = {
{.width = 64, .height = 64},
{.width = 64, .height = 32},
{.width = 32, .height = 64},
{.width = 32, .height = 32},
{.width = 32, .height = 16},
{.width = 32, .height = 8},
{.width = 16, .height = 32},
{.width = 16, .height = 16},
{.width = 16, .height = 8},
{.width = 8, .height = 32},
{.width = 8, .height = 16},
{.width = 8, .height = 8},
};
struct OamShape* FindSingleShape(int tileX, int tileY, int imageTileWidth, int imageTileHeight) {
int chunkWidth = (imageTileWidth - tileX)*8;
int chunkHeight = (imageTileHeight - tileY)*8;
OamDim found = {0};
for(int i = 0; i < ARRAY_COUNT(oamSizes); i++) {
if((chunkWidth >= oamSizes[i].width) && (chunkHeight >= oamSizes[i].height)) {
found = oamSizes[i];
break;
}
}
if((found.width + found.height) > 0) {
struct OamShape *res = calloc(1, sizeof(struct OamShape));
res->offX = tileX*8;
res->offY = tileY*8;
res->width = found.width;
res->height = found.height;
return res;
} else {
return NULL;
}
}
struct OamShape *FindOamShapes(int imageWidth, int imageHeight) {
int tileWidth = imageWidth / 8;
int tileHeight = imageHeight / 8;
char *collBuffer = calloc(1, (tileWidth * tileHeight));
int pitch = tileWidth;
struct OamShape* head = FindSingleShape(0, 0, tileWidth, tileHeight);
struct OamShape* curr = head;
// Set tiles as set
for(int y = 0; y < curr->height / 8; y++) {
char *pos = &collBuffer[(curr->offY / 8 + y) * pitch + curr->offX / 8];
memset(pos, 1, curr->width / 8);
}
for(int x = 0; x < tileWidth; x++) {
// Find next shape position
int y = tileHeight;
for(; y > 0 && collBuffer[(y - 1)*pitch + x] == 0; y--)
;
if(y != tileHeight) {
curr->next = FindSingleShape(x, y, tileWidth, tileHeight);
curr = curr->next;
// Set tiles as set
for(int y = 0; y < curr->height / 8; y++) {
char *pos = &collBuffer[(curr->offY / 8 + y) * pitch + curr->offX / 8];
memset(pos, 1, curr->width / 8);
}
// We did not yet reach the bottom of the image
// so we have to continue looking at the current x pos
--x;
}
}
int visitedTiles = 0;
while(visitedTiles < tileWidth * tileHeight) {
if(collBuffer[visitedTiles] != 1)
break;
visitedTiles++;
}
if(visitedTiles != tileWidth * tileHeight)
FATAL_ERROR("Could not find all OAM shapes in the image");
free(collBuffer);
return head;
}
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool ignoreEmptyTrailingTiles, bool splitIntoOamShapes)
{
if (image->width % 8 != 0)
FATAL_ERROR("The width in pixels (%d) isn't a multiple of 8.\n", image->width);
if (image->height % 8 != 0)
FATAL_ERROR("The height in pixels (%d) isn't a multiple of 8.\n", image->height);
int tilesWidth = image->width / 8;
int tilesHeight = image->height / 8;
int imgWidthTiles = image->width / 8;
int imgHeightTiles = image->height / 8;
if (tilesWidth % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", tilesWidth, metatileWidth);
if (imgWidthTiles % metatileWidth != 0)
FATAL_ERROR("The width in tiles (%d) isn't a multiple of the specified metatile width (%d)", imgWidthTiles, metatileWidth);
if (tilesHeight % metatileHeight != 0)
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", tilesHeight, metatileHeight);
if (imgHeightTiles % metatileHeight != 0)
FATAL_ERROR("The height in tiles (%d) isn't a multiple of the specified metatile height (%d)", imgHeightTiles, metatileHeight);
int maxNumTiles = tilesWidth * tilesHeight;
int maxNumTiles = imgWidthTiles * imgHeightTiles;
if (numTiles == 0)
numTiles = maxNumTiles;
else if (numTiles > maxNumTiles)
FATAL_ERROR("The specified number of tiles (%d) is greater than the maximum possible value (%d).\n", numTiles, maxNumTiles);
int tileSize = (8*8 * bitDepth) / 8;
int bufferSize = numTiles * tileSize;
unsigned char *buffer = malloc(bufferSize);
if (buffer == NULL)
FATAL_ERROR("Failed to allocate memory for pixels.\n");
int metatilesWide = tilesWidth / metatileWidth;
int metatilesWide = imgWidthTiles / metatileWidth;
switch (bitDepth) {
case 1:
@@ -293,13 +408,71 @@ void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int m
int outfileSize = bufferSize;
if(ignoreEmptyTrailingTiles) {
int tileSize = (8*8 * bitDepth) / 8;
if(splitIntoOamShapes) {
if(ignoreEmptyTrailingTiles)
FATAL_ERROR("It is not supported to use -ignore_trailing and -split_into_oam_shapes simultaneously.");
// TODO: Add 8bpp support
if(bitDepth != 4)
FATAL_ERROR("Only 4bpp images supported for -split_into_oam_shapes");
unsigned char* scratchBuffer = malloc(bufferSize);
if(scratchBuffer) {
/* Split image into shapes */
struct OamShape* head = FindOamShapes(image->width, image->height);
#if 0
/* Debug */
printf("OAM Shapes (%d, %d):\n", image->width, image->height);
struct OamShape* temp = head;
while(temp) {
printf("\t=> %d %d %d %d\n", temp->offX, temp->offY, temp->width, temp->height);
temp = temp->next;
}
#endif
/* Copy the tiles over in the correct order */
struct OamShape* curr = head;
int dstIndex = 0;
while(curr) {
int imgPitch = imgWidthTiles * tileSize;
int srcIndex = (curr->offY / 8) * imgPitch + (curr->offX / 8) * tileSize;
for(int y = 0; y < curr->height / 8; y++) {
int copySize = (curr->width / 8) * tileSize;
memcpy(&scratchBuffer[dstIndex],
&buffer[srcIndex],
copySize);
srcIndex += imgPitch;
dstIndex += copySize;
}
curr = curr->next;
}
/* Copy the scratch buffer back into the original buffer */
memcpy(buffer, scratchBuffer, bufferSize);
/* Free the shape list*/
curr = head;
while(curr) {
struct OamShape* old = curr;
curr = old->next;
free(old);
};
free(scratchBuffer);
} else {
FATAL_ERROR("Couldn't allocate buffer for splitting into OAM shapes");
}
}
if(ignoreEmptyTrailingTiles) {
if((outfileSize % tileSize) != 0)
FATAL_ERROR("The output buffer size (0x%X) is not a multiple of the tileSize (0x%X).", outfileSize, tileSize);
unsigned char* bufferEnd = buffer + (outfileSize-1);
unsigned char* bufferEnd = buffer + (bufferSize-1);
int checksum = 0;
+1 -1
View File
@@ -28,7 +28,7 @@ struct Image {
};
void ReadImage(char *path, int tilesWidth, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors);
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool ignoreTrailingTiles);
void WriteImage(char *path, int numTiles, int bitDepth, int metatileWidth, int metatileHeight, struct Image *image, bool invertColors, bool ignoreTrailingTiles, bool splitIntoOamShapes);
void FreeImage(struct Image *image);
void ReadGbaPalette(char *path, struct Palette *palette);
void WriteGbaPalette(char *path, struct Palette *palette);
+2
View File
@@ -6,6 +6,8 @@
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_COUNT(arr) (sizeof(arr) / sizeof((arr)[0]))
#ifdef _MSC_VER
#define FATAL_ERROR(format, ...) \
+12 -6
View File
@@ -62,7 +62,7 @@ void ConvertPngToGba(char *inputPath, char *outputPath, struct PngToGbaOptions *
ReadPng(inputPath, &image);
WriteImage(outputPath, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->ignoreTrailingTiles);
WriteImage(outputPath, options->numTiles, options->bitDepth, options->metatileWidth, options->metatileHeight, &image, !image.hasPalette, options->ignoreTrailingTiles, options->splitIntoOamShapes);
FreeImage(&image);
}
@@ -150,12 +150,13 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
{
char *outputFileExtension = GetFileExtensionAfterDot(outputPath);
int bitDepth = outputFileExtension[0] - '0';
struct PngToGbaOptions options;
struct PngToGbaOptions options = {0};
options.numTiles = 0;
options.bitDepth = bitDepth;
options.metatileWidth = 1;
options.metatileHeight = 1;
options.ignoreTrailingTiles = false;
options.splitIntoOamShapes = false;
for (int i = 3; i < argc; i++)
{
@@ -167,6 +168,11 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
// These shall be ignored when -ignore_trailing is set.
options.ignoreTrailingTiles = true;
}
else if(strcmp(option, "-split_into_oam_shapes") == 0) {
// Tilesets in Sonic Advance 1 and 2 can have empty tiles at the end.
// These shall be ignored when -ignore_trailing is set.
options.splitIntoOamShapes = true;
}
else if (strcmp(option, "-num_tiles") == 0)
{
if (i + 1 >= argc)
@@ -217,7 +223,7 @@ void HandlePngToGbaCommand(char *inputPath, char *outputPath, int argc, char **a
void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadPngPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
@@ -225,7 +231,7 @@ void HandlePngToJascPaletteCommand(char *inputPath, char *outputPath, int argc U
void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadPngPalette(inputPath, &palette);
WriteGbaPalette(outputPath, &palette);
@@ -233,7 +239,7 @@ void HandlePngToGbaPaletteCommand(char *inputPath, char *outputPath, int argc UN
void HandleGbaToJascPaletteCommand(char *inputPath, char *outputPath, int argc UNUSED, char **argv UNUSED)
{
struct Palette palette = {};
struct Palette palette = {0};
ReadGbaPalette(inputPath, &palette);
WriteJascPalette(outputPath, &palette);
@@ -266,7 +272,7 @@ void HandleJascToGbaPaletteCommand(char *inputPath, char *outputPath, int argc,
}
}
struct Palette palette = {};
struct Palette palette = {0};
ReadJascPalette(inputPath, &palette);
+1
View File
@@ -20,6 +20,7 @@ struct PngToGbaOptions {
int metatileWidth;
int metatileHeight;
bool ignoreTrailingTiles;
bool splitIntoOamShapes;
};
#endif // OPTIONS_H