Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change: Support decoding of NewGRF with out-of-order v2 container and 32bpp-only files. #29

Merged
merged 2 commits into from
Dec 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/grfcodec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <sys/stat.h>
#include <getopt.h>
#include <algorithm>
#include <map>

#ifdef __MINGW32__
#include <io.h>
Expand Down Expand Up @@ -651,6 +652,31 @@ static int encode(const char *file, const char *dir, int compress, int *colourma
return 0;
}

std::map<uint, off_t> _sprite_offsets{};

static void read_grf_sprite_offsets(FILE *grf)
{
_sprite_offsets.clear();

const char action[] = "Reading sprite offsets";
uint32_t offset = readdword(action, grf);
off_t pos = ftell(grf);
fseek(grf, offset, SEEK_CUR);

uint id = 0, count = 0;
while ((id = readdword(action, grf)) != 0) {
if (_sprite_offsets.count(id) == 0)
_sprite_offsets[id] = ftell(grf) - 4;

fseek(grf, readdword(action, grf), SEEK_CUR);
count++;
}

printf("Found %zu / %u container chunks\n", _sprite_offsets.size(), count);

fseek(grf, pos, SEEK_SET);
}

static int decode(const char *file, const char *dir, const U8 *palette, int box, int width, int height, int *colourmap, int useplaintext)
{
int count, result, lastpct = -1;
Expand Down Expand Up @@ -694,14 +720,13 @@ static int decode(const char *file, const char *dir, const U8 *palette, int box,
cfread(action, buffer, 1, sizeof(buffer), grf);
int grfcontversion = memcmp(buffer, header, sizeof(header)) == 0 ? 2 : 1;

U32 dataoffset = 0;
printf("Found grf container version %d\n", grfcontversion);
if (grfcontversion == 1) {
fseek(grf, 0, SEEK_SET);
} else {
dataoffset = sizeof(header) + 4 + readdword(action, grf); // GRF data offset
read_grf_sprite_offsets(grf);
fgetc(grf); // Compression
}
printf("Found grf container version %d\n", grfcontversion);

// We do the 'file' and 'writer' seperate to make
// this a little bit less messy
Expand Down Expand Up @@ -764,7 +789,7 @@ static int decode(const char *file, const char *dir, const U8 *palette, int box,
pcx->newsprite();
if (pcx32 != NULL) pcx32->newsprite();

result = decodesprite(grf, pcx, pcx32, &writer, count, &dataoffset, grfcontversion);
result = decodesprite(grf, pcx, pcx32, &writer, count, grfcontversion);
writer.flush();
count++;
} while (result);
Expand Down
18 changes: 9 additions & 9 deletions src/sprites.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
\*****************************************/

#include <stdlib.h>
#include <map>

#include "sprites.h"
#include "grfcomm.h"
Expand Down Expand Up @@ -241,7 +242,9 @@ static void writesprite(bool first, spritestorage *image_writer, spriteinfowrite
image_writer->spritedone(info.xdim, info.ydim);
}

int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, spriteinfowriter *writer, int spriteno, U32 *dataoffset, int grfcontversion)
extern std::map<uint, off_t> _sprite_offsets;

int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, spriteinfowriter *writer, int spriteno, int grfcontversion)
{
static const char *action = "decoding sprite";
unsigned long size, datasize, inbufsize, outbufsize, startpos, returnpos = 0;
Expand All @@ -268,7 +271,8 @@ int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, sprit
id = readdword(action, grf);
returnpos = ftell(grf);

fseek(grf, *dataoffset, SEEK_SET);
uint32_t dataoffset = _sprite_offsets[id];
fseek(grf, dataoffset, SEEK_SET);
}

long result;
Expand Down Expand Up @@ -302,7 +306,6 @@ int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, sprit
result = 1;

if (returnpos == 0) return result;
*dataoffset = startpos + size + 1;
break;
}

Expand Down Expand Up @@ -394,14 +397,14 @@ int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, sprit
printf("\nError: cannot decode 32bpp sprites to pcx\n");
exit(2);
}
writesprite(false, imgrgba, writer, imgbuffer, info);
writesprite(i == 0, imgrgba, writer, imgbuffer, info);
} else if (info.depth==DEPTH_MASK) {
if (imgrgba == NULL) {
printf("\nError: cannot decode 32bpp sprites to pcx\n");
exit(2);
}
info.depth=DEPTH_32BPP;
writesprite(false, imgrgba, writer, imgbuffer, info);
writesprite(i == 0, imgrgba, writer, imgbuffer, info);
info.depth=DEPTH_MASK;
writesprite(false, imgpal, writer, imgbuffer, info);
} else if (info.depth==DEPTH_8BPP) {
Expand All @@ -422,10 +425,7 @@ int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, sprit
free(outbuffer);
free(imgbuffer);

if (returnpos != 0) {
*dataoffset = startpos + inbufsize;
if (grfcontversion == 2 && HASTRANSPARENCY(info.info)) *dataoffset += 4;
} else {
if (returnpos == 0) {
returnpos = startpos + inbufsize;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion src/sprites.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class spritestorage {

extern int maxx, maxy, maxs;

int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, spriteinfowriter *writer, int spriteno, U32 *dataoffset, int grfcontversion);
int decodesprite(FILE *grf, spritestorage *imgpal, spritestorage *imgrgba, spriteinfowriter *writer, int spriteno, int grfcontversion);

long getlasttilesize();
long encodetile(U8 **compressed_data, long *uncompressed_size, const CommonPixel *image, long imgsize, int sx, int sy, SpriteInfo inf, int docompress, int spriteno, bool has_mask, bool rgba, int grfcontversion);
Expand Down