mirror of
https://github.com/SAT-R/sa2.git
synced 2026-10-28 09:43:38 +00:00
Add -split_into_oam_shapes parameter
This commit is contained in:
1 parent
358bbf0555
commit
924e32eb63
5 files changed
+204
-22
No files matched your search
Vendored
+188
-15
@@ -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;
|
||||
|
||||
|
||||
Vendored
+1
-1
@@ -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);
|
||||
|
||||
Vendored
+2
@@ -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, ...) \
|
||||
|
||||
Vendored
+12
-6
@@ -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);
|
||||
|
||||
|
||||
Vendored
+1
@@ -20,6 +20,7 @@ struct PngToGbaOptions {
|
||||
int metatileWidth;
|
||||
int metatileHeight;
|
||||
bool ignoreTrailingTiles;
|
||||
bool splitIntoOamShapes;
|
||||
};
|
||||
|
||||
#endif // OPTIONS_H
|
||||
Reference in new issue
Block a user