diff --git a/NEWS b/NEWS index 2d921c9..2eedc73 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,7 @@ What's new in thtk master - Support for TH18, TH185, TH19 (full) has been added. - Support th143/bestshot.anm - The -u flag can now be used to extract anm files with duplicate filenames. +- Images can now be extracted without adding a border using the -b flag. #### thanm.old - Will be removed in the next release. diff --git a/thanm/image.c b/thanm/image.c index e0a7171..37b6538 100644 --- a/thanm/image.c +++ b/thanm/image.c @@ -183,6 +183,62 @@ format_to_rgba( return (unsigned char*)out; } +void +png_read_mem( + image_t *image, + void *data, + size_t len) +{ + png_image png = { + .version = PNG_IMAGE_VERSION, + .opaque = NULL + }; + png_image_begin_read_from_memory(&png, data, len); + if (PNG_IMAGE_FAILED(png)) { + fprintf(stderr, "%s: error decoding png: %s\n", argv0, png.message); + exit(1); + } + png.format = PNG_FORMAT_RGBA; + + image->width = png.width; + image->height = png.height; + image->format = FORMAT_RGBA8888; + image->data = malloc(PNG_IMAGE_SIZE(png)); + + png_image_finish_read(&png, 0, image->data, 0, 0); + if (PNG_IMAGE_FAILED(png)) { + fprintf(stderr, "%s: error decoding png: %s\n", argv0, png.message); + exit(1); + } +} + +void * +png_write_mem( + image_t *image, + size_t *outsize) +{ + png_image png; + memset(&png, 0, sizeof(png)); + png.version = PNG_IMAGE_VERSION; + png.width = image->width; + png.height = image->height; + png.format = PNG_FORMAT_RGBA; + + png_image_write_to_memory(&png, 0, outsize, 0, image->data, 0, 0); + if (PNG_IMAGE_FAILED(png)) { + fprintf(stderr, "%s: error encoding png: %s\n", argv0, png.message); + exit(1); + } + void *out = malloc(*outsize); + png_image_write_to_memory(&png, out, outsize, 0, image->data, 0, 0); + if (PNG_IMAGE_FAILED(png)) { + fprintf(stderr, "%s: error encoding png: %s\n", argv0, png.message); + exit(1); + } + + return out; +} + image_t* png_read( const char* filename) diff --git a/thanm/image.h b/thanm/image.h index 22da432..d55ef87 100644 --- a/thanm/image.h +++ b/thanm/image.h @@ -59,6 +59,17 @@ typedef struct { format_t format; } image_t; +void +png_read_mem( + image_t *image, + void *data, + size_t len); + +void * +png_write_mem( + image_t *image, + size_t *outsize); + image_t* png_read( const char* filename); diff --git a/thanm/thanm.1 b/thanm/thanm.1 index 298d0e8..3f662b2 100644 --- a/thanm/thanm.1 +++ b/thanm/thanm.1 @@ -24,7 +24,7 @@ .\" OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS .\" SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH .\" DAMAGE. -.Dd August 28, 2023 +.Dd August 30, 2023 .Dt THANM 1 .Os .Sh NAME @@ -32,7 +32,7 @@ .Nd Touhou sprite archive tool .Sh SYNOPSIS .Nm -.Op Fl Vfu +.Op Fl Vfub .Op Oo Fl l | x | r | c Oc Ar version .Oo Fl m Ar anmmap Oc Ns Ar ... .Oo Fl s Ar symbols Oc @@ -45,15 +45,15 @@ The following commands are available: .Bl -tag -width Ds .It Nm Fl l Ar version Oo Fl fu Oc Oo Fl m Ar anmmap Oc Ns Ar ... Ar archive Displays a specification of the archive. -.It Nm Fl x Ar version Oo Fl fu Oc Ar archive Op Ar +.It Nm Fl x Ar version Oo Fl fub Oc Ar archive Op Ar Extracts image files. If no files are specified, all files are extracted. -.It Nm Fl r Ar version Oo Fl f Oc Ar archive Ar name Ar file +.It Nm Fl r Ar version Oo Fl fb Oc Ar archive Ar name Ar file Replaces an entry in the archive. The name can be obtained by the .Fl l command. -.It Nm Fl c Ar version Oo Fl f Oc Oo Fl m Ar anmmap Oc Ns Ar ... Oo Fl s Ar symbols Oc Ar archive Ar input +.It Nm Fl c Ar version Oo Fl fb Oc Oo Fl m Ar anmmap Oc Ns Ar ... Oo Fl s Ar symbols Oc Ar archive Ar input Creates a new archive from a specification obtained by the .Fl l command. @@ -82,6 +82,14 @@ file. The .Fl u option gives unique filenames to extracted images. +.It b +The +.Fl b +option disables adding/removing a border to images with x/y offset. +For TH19, +.Nm +won't recompress the image unless it needs to add a border. +JPEGs are never recompressed, regardless of the setting. .El .Sh EXIT STATUS The diff --git a/thanm/thanm.c b/thanm/thanm.c index 589ad33..2b3c919 100644 --- a/thanm/thanm.c +++ b/thanm/thanm.c @@ -47,6 +47,7 @@ anmmap_t* g_anmmap = NULL; unsigned int option_force = 0; unsigned int option_print_offsets = 0; unsigned int option_unique_filenames = 0; +unsigned int option_dont_add_offset_border = 0; /* SPECIAL FORMATS: * 'o' - offset (for labels) @@ -1443,10 +1444,12 @@ util_total_entry_size( unsigned int height = 0; if (entry && entry->header->hasdata) { - if (entry->header->x + entry->thtx->w > width) - width = entry->header->x + entry->thtx->w; - if (entry->header->y + entry->thtx->h > height) - height = entry->header->y + entry->thtx->h; + const uint32_t ox = option_dont_add_offset_border ? 0 : entry->header->x; + const uint32_t oy = option_dont_add_offset_border ? 0 : entry->header->y; + if (ox + entry->thtx->w > width) + width = ox + entry->thtx->w; + if (oy + entry->thtx->h > height) + height = oy + entry->thtx->h; } *widthptr = width; @@ -1498,19 +1501,21 @@ anm_replace( unsigned int y; unsigned char* converted_data = format_from_rgba((uint32_t*)image->data, width * height, formats[f]); + const uint32_t ox = option_dont_add_offset_border ? 0 : entry->header->x; + const uint32_t oy = option_dont_add_offset_border ? 0 : entry->header->y; if (anmfp) { - for (y = entry->header->y; y < entry->header->y + entry->thtx->h; ++y) { + for (y = oy; y < oy + entry->thtx->h; ++y) { if (!file_seek(anmfp, - offset + entry->header->thtxoffset + sizeof(thtx_header_t) + (y - entry->header->y) * entry->thtx->w * format_Bpp(formats[f]))) + offset + entry->header->thtxoffset + sizeof(thtx_header_t) + (y - oy) * entry->thtx->w * format_Bpp(formats[f]))) exit(1); - if (!file_write(anmfp, converted_data + y * width * format_Bpp(formats[f]) + entry->header->x * format_Bpp(formats[f]), entry->thtx->w * format_Bpp(formats[f]))) + if (!file_write(anmfp, converted_data + y * width * format_Bpp(formats[f]) + ox * format_Bpp(formats[f]), entry->thtx->w * format_Bpp(formats[f]))) exit(1); } } else { - for (y = entry->header->y; y < entry->header->y + entry->thtx->h; ++y) { - memcpy(entry->data + (y - entry->header->y) * entry->thtx->w * format_Bpp(formats[f]), - converted_data + y * width * format_Bpp(formats[f]) + entry->header->x * format_Bpp(formats[f]), + for (y = oy; y < oy + entry->thtx->h; ++y) { + memcpy(entry->data + (y - oy) * entry->thtx->w * format_Bpp(formats[f]), + converted_data + y * width * format_Bpp(formats[f]) + ox * format_Bpp(formats[f]), entry->thtx->w * format_Bpp(formats[f])); } } @@ -1526,6 +1531,20 @@ anm_replace( free(image); } +static unsigned char * +entry_to_rgba( + anm_entry_t *entry, + int is_png) +{ + if (is_png) { + image_t image; + png_read_mem(&image, entry->data, entry->thtx->size); + return image.data; + } else { + return format_to_rgba(entry->data, entry->thtx->w * entry->thtx->h, entry->thtx->format); + } +} + static void anm_extract( anm_entry_t* entry, @@ -1556,14 +1575,22 @@ anm_extract( util_makepath(filename); + const uint32_t ox = option_dont_add_offset_border ? 0 : entry->header->x; + const uint32_t oy = option_dont_add_offset_border ? 0 : entry->header->y; + int is_png = 0; + /* NEWHU: 19 */ if (version == 19) { - FILE* stream = fopen(filename, "wb"); - if (stream) { - fwrite(entry->thtx->data, 1, entry->thtx->size, stream); - fclose(stream); + if (png_identify(entry->thtx->data, entry->thtx->size) && (ox || oy)) { + is_png = 1; + } else { + FILE* stream = fopen(filename, "wb"); + if (stream) { + fwrite(entry->thtx->data, 1, entry->thtx->size, stream); + fclose(stream); + } + return; } - return; } image.data = malloc(image.width * image.height * 4); @@ -1571,10 +1598,10 @@ anm_extract( memset(image.data, 0xff, image.width* image.height * 4); for (f = 0; f < sizeof(formats) / sizeof(formats[0]); ++f) { if (formats[f] == entry->thtx->format) { - unsigned char* temp_data = format_to_rgba(entry->data, entry->thtx->w * entry->thtx->h, entry->thtx->format); - for (y = entry->header->y; y < entry->header->y + entry->thtx->h; ++y) { - memcpy(image.data + y * image.width * 4 + entry->header->x * 4, - temp_data + (y - entry->header->y) * entry->thtx->w * 4, + unsigned char* temp_data = entry_to_rgba(entry, is_png); + for (y = oy; y < oy + entry->thtx->h; ++y) { + memcpy(image.data + y * image.width * 4 + ox * 4, + temp_data + (y - oy) * entry->thtx->w * 4, entry->thtx->w * 4); } free(temp_data); @@ -2154,7 +2181,7 @@ print_usage(void) #else #define USAGE_LIBPNGFLAGS "" #endif - printf("Usage: %s [-Vfu] [[-l" USAGE_LIBPNGFLAGS "] VERSION] [-m ANMMAP]... [-s SYMBOLS] ARCHIVE ...\n" + printf("Usage: %s [-Vfub] [[-l" USAGE_LIBPNGFLAGS "] VERSION] [-m ANMMAP]... [-s SYMBOLS] ARCHIVE ...\n" "Options:\n" " -l VERSION ARCHIVE list archive\n" #ifdef HAVE_LIBPNG @@ -2167,6 +2194,7 @@ print_usage(void) " -V display version information and exit\n" " -f ignore errors when possible\n" " -u give unique filenames to extracted images\n" + " -b don't add/remove a border to images with x/y offset\n" " -o add address information for for ANM instructions\n" "VERSION can be:\n" " 6, 7, 8, 9, 95, 10, 103, 11, 12, 125, 128, 13, 14, 143, 15, 16, 165, 17, 18, 185 or 19\n" @@ -2191,7 +2219,7 @@ main( #ifdef HAVE_LIBPNG "x:r:c:s:" #endif - "Vfu"; + "Vfub"; int command = -1; FILE* in; @@ -2254,6 +2282,9 @@ main( case 'u': option_unique_filenames = 1; break; + case 'b': + option_dont_add_offset_border = 1; + break; case 'o': if (command == 'c') { fprintf(stderr, "%s: 'x' option can't be used when creating ANM archive\n", argv0);