From: chaoskagami Date: Fri, 17 Oct 2014 01:42:12 +0000 (-0400) Subject: Integrate an experimental GIM image conversion tool - it breaks on a lot, and everyth... X-Git-Url: https://chaos.moe/g/?a=commitdiff_plain;h=b7c2e515e8c242593dd88b968923c2b39180a409;p=console%2FRCOMage.git Integrate an experimental GIM image conversion tool - it breaks on a lot, and everything is upside down when it does work, but meh, cool beans. --- diff --git a/INSTALL b/INSTALL index 7d1c323..2099840 100644 --- a/INSTALL +++ b/INSTALL @@ -1,8 +1,8 @@ Installation Instructions ************************* -Copyright (C) 1994, 1995, 1996, 1999, 2000, 2001, 2002, 2004, 2005, -2006, 2007, 2008, 2009 Free Software Foundation, Inc. +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. Copying and distribution of this file, with or without modification, are permitted in any medium without royalty provided the copyright @@ -12,8 +12,8 @@ without warranty of any kind. Basic Installation ================== - Briefly, the shell commands `./configure; make; make install' should -configure, build, and install this package. The following + Briefly, the shell command `./configure && make && make install' +should configure, build, and install this package. The following more-detailed instructions are generic; see the `README' file for instructions specific to this package. Some packages provide this `INSTALL' file but do not implement all of the features documented @@ -226,6 +226,11 @@ order to use an ANSI C compiler: and if that doesn't work, install pre-built binaries of GCC for HP-UX. + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot parse its `' header file. The option `-nodtk' can be used as a workaround. If GNU CC is not installed, it is therefore recommended @@ -304,9 +309,10 @@ causes the specified `gcc' to be used as the C compiler (unless it is overridden in the site shell script). Unfortunately, this technique does not work for `CONFIG_SHELL' due to -an Autoconf bug. Until the bug is fixed you can use this workaround: +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: - CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash `configure' Invocation ====================== @@ -362,4 +368,3 @@ operates. `configure' also accepts some other, not widely useful, options. Run `configure --help' for more details. - diff --git a/compile b/compile new file mode 100755 index 0000000..531136b --- /dev/null +++ b/compile @@ -0,0 +1,347 @@ +#! /bin/sh +# Wrapper for compilers which do not understand '-c -o'. + +scriptversion=2012-10-14.11; # UTC + +# Copyright (C) 1999-2013 Free Software Foundation, Inc. +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/src/Makefile.am b/src/Makefile.am index 0cc9d7f..41f4ac4 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -38,7 +38,9 @@ rcomage_SOURCES = \ vsmx.c \ vsmx.h \ rlzpack.h \ - strfuncs.h + strfuncs.h \ + gimtool.c \ + gimtool.h rcomage_LDADD = $(top_builddir)/7z/lib7z.la \ diff --git a/src/gimtool.c b/src/gimtool.c new file mode 100644 index 0000000..11611e1 --- /dev/null +++ b/src/gimtool.c @@ -0,0 +1,477 @@ +//* *// +//* gimconv.c - Rough GIM <-> BMP converter. Not fully complete. *// +//* Converts GIM to BMP and viceversa, intended to be used within *// +//* the RCOmage fork I'm coding *// + +//* Mostly based on the code in GIMSharp - useful, but C#-y. That's *// +//* not a good thing, IMO. There's far too much support code for *// +//* data reads in C-ish ways, and using streams like fread *// +//* *// + +//* Things being derpy at the moment - Argb8888. Why? IDK. *// + +#include +#include +#include +#include + +#include "gimtool.h" + +char* unswizzle(char* data, int width, int height, int bpp) { + width = (width * bpp) >> 3; + + char* dest = (char*)calloc(sizeof(char), width*height); + + int rowblocks = (width / 16); + + int dstoff = 0; + + int x, y; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + int blockX = x / 16; + int blockY = y / 8; + + int blockIndex = blockX + ((blockY) * rowblocks); + int blockAddress = blockIndex * 16 * 8; + + dest[dstoff] = data[blockAddress + (x - blockX * 16) + ((y - blockY * 8) * 16)]; + dstoff++; + } + } + + return dest; +} + +void dump_bitmap(char* fname, char* data32bpp, int width, int height) { + + // Bitmap info. + bitmapv4_header bmp_inh; + + bmp_inh.bmpinfohead_size = sizeof(bitmapv4_header); + bmp_inh.width = width; + bmp_inh.height = height; + bmp_inh.color_planes = 1; + bmp_inh.bpp = 32; + bmp_inh.compression = 3; + bmp_inh.image_size = width*32*height; + bmp_inh.ppm_w = 0; + bmp_inh.ppm_h = 0; + bmp_inh.colors = 0; + bmp_inh.important_col = 0; + bmp_inh.redmask = 0x00ff0000; + bmp_inh.bluemask = 0x0000ff00; + bmp_inh.greenmask = 0x000000ff; + bmp_inh.alphamask = 0xff000000; + bmp_inh.colsptype = 0x206E6957; + bmp_inh.reserved0 = bmp_inh.reserved1 = bmp_inh.reserved2 = bmp_inh.reserved3 = bmp_inh.reserved4 = bmp_inh.reserved5 = bmp_inh.reserved6 = bmp_inh.reserved7 = bmp_inh.reserved8 = 0; + bmp_inh.gamma_r = 0; + bmp_inh.gamma_g = 0; + bmp_inh.gamma_b = 0; + + + // File header. + bitmap_header bmp_h; + + bmp_h.magic1 = 'B'; + bmp_h.magic2 = 'M'; + bmp_h.reserved = 0; + bmp_h.offset = sizeof(bitmap_header) + sizeof(bitmapv4_header); + bmp_h.filesize = sizeof(bitmap_header) + sizeof(bitmapv4_header) + (width*height*4); + + // Write shit out. + FILE* out = fopen(fname, "wb"); + fwrite(&bmp_h, 1, sizeof(bitmap_header), out); + fwrite(&bmp_inh, 1, sizeof(bitmapv4_header), out); + fwrite(data32bpp, 1, (width*height*4), out); + fclose(out); +} + +// Used for palletes too. +char* decode_Rgb565(char* data, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + // Cast data to a ushort pointer. + uint16_t* pointer = (uint16_t*)data; + + int i; + for(i=0; i < size; i++) { + output[i*4 + 3] = 0xFF; + output[i*4 + 2] = (char)(((pointer[i] >> 0) & 0x1F) * 0xFF / 0x1F); + output[i*4 + 1] = (char)(((pointer[i] >> 5) & 0x3F) * 0xFF / 0x3F); + output[i*4 + 0] = (char)(((pointer[i] >> 11) & 0x1F) * 0xFF / 0x1F); + } + + return output; +} + +char* decode_Argb1555(char* data, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + // Cast data to a ushort pointer. + uint16_t* pointer = (uint16_t*)data; + + int i; + for(i=0; i < size; i++) { + output[i*4 + 3] = (char)(((pointer[i] >> 15) & 0x01) * 0xFF); + output[i*4 + 2] = (char)(((pointer[i] >> 0) & 0x1F) * 0xFF / 0x1F); + output[i*4 + 1] = (char)(((pointer[i] >> 5) & 0x1F) * 0xFF / 0x1F); + output[i*4 + 0] = (char)(((pointer[i] >> 10) & 0x1F) * 0xFF / 0x1F); + } + + return output; +} + +char* decode_Argb4444(char* data, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + // Cast data to a ushort pointer. + uint16_t* pointer = (uint16_t*)data; + + int i; + for(i=0; i < size; i++) { + output[i*4 + 3] = (char)(((pointer[i] >> 12) & 0x0F) * 0xFF / 0x0F); + output[i*4 + 2] = (char)(((pointer[i] >> 0) & 0x0F) * 0xFF / 0x0F); + output[i*4 + 1] = (char)(((pointer[i] >> 4) & 0x0F) * 0xFF / 0x0F); + output[i*4 + 0] = (char)(((pointer[i] >> 8) & 0x0F) * 0xFF / 0x0F); + } + + return output; +} + +char* decode_Argb8888(char* data, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + // Cast data to a ushort pointer. + uint32_t* pointer = (uint32_t*)data; + uint32_t* out = (uint32_t*)output; + + int i; + for(i=0; i < size; i++) { + out[i] = pointer[i]; + } + + return output; +} + +char* decode_Index4(char* data, char* palette, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + uint32_t* out = (uint32_t*)output; + uint32_t* pal = (uint32_t*)palette; + + int i; + for(i=0; i < size; i += 2) { + int px1 = data[i / 2] & 0xF; + int px2 = data[i / 2] >> 4 & 0xF; + out[i] = pal[px1]; + out[i+1] = pal[px2]; + } + + return output; +} + +char* decode_Index8(char* data, char* palette, int width, int height) { + int size = (width * height); + + char* output = (char*)calloc(sizeof(char), 4*size); // 32bpp so 4 + + uint32_t* out = (uint32_t*)output; + uint32_t* pal = (uint32_t*)palette; + + int i; + for(i=0; i < size; i++) { + out[i] = pal[data[i]]; + } + + return output; +} + +OutData* ReadData(char* data, int length) { + + // Data Used Here + int32_t offset = 0x10; + int32_t prevOffset = offset; + int32_t textureOffset = 0; + int32_t palleteOffset = 0; + uint16_t width = 0, height = 0; + char datFmt = Unknown; + char palFmt = Unknown; + uint16_t pal_ents = 0; + int32_t eofOffset = 0; + int32_t hasMeta = 0; + int32_t swizzled = 0; + + // Read all the meta blocks. + while (offset < length) { + + // EOF Block + if (data[offset] == 0x02) { + eofOffset = ((int32_t*)(&data[offset+0x04]))[0] + 16; + + //fprintf(stderr, "[%x] eofOffset: %x\n", offset, eofOffset); + + offset += ((int32_t*)(&data[offset+0x08]))[0]; + } + + // Metadata Chunk + else if (data[offset] == 0x03) { + // We actually skip this. + offset += ((int32_t*)(&data[offset+0x08]))[0]; + } + + // Texture Data Chunk + else if (data[offset] == 0x04) { + datFmt = (char)data[offset + 0x14]; + + swizzled = ((uint16_t*)(&data[offset+0x16]))[0]; + + width = ((uint16_t*)(&data[offset+0x18]))[0]; + height = ((uint16_t*)(&data[offset+0x1A]))[0]; + + if (width % 16 != 0) { + width += (16 - (width % 16)); + } + if (height % 8 != 0) { + height += (8 - (height % 8)); + } + + + textureOffset = offset + 0x50; + + //fprintf(stderr, "[%x] texture: f:%u w:%i h:%i swz:%i off:%x\n", offset, datFmt, width, height, swizzled, textureOffset); + + offset += ((int32_t*)(&data[offset+0x08]))[0]; + + } + + // Pallete Chunk + else if (data[offset] == 0x05) { + palFmt = (char)data[offset + 0x14]; + + pal_ents = ((uint16_t*)(&data[offset+0x18]))[0]; + + palleteOffset = offset + 0x50; + + //fprintf(stderr, "[%x] pallete: f:%u ent:%u off:%x\n", offset, palFmt, pal_ents, palleteOffset); + + offset += ((int32_t*)(&data[offset+0x08]))[0]; + + } + + // Metadata Chunk + else if (data[offset] == 0xff) { + hasMeta = 1; + size_t metaOff = 0x10; + + // Seek to end of string for each piece of data + int i=0; + + while(data[offset+metaOff+i]) { + if(data[offset+metaOff+i] == 0) + break; + i++; + } + metaOff += i + 1; i = 0; + + while(data[offset+metaOff+i]) { + if(data[offset+metaOff+i] == 0) + break; + i++; + } + metaOff += i + 1; i = 0; + + while(data[offset+metaOff+i]) { + if(data[offset+metaOff+i] == 0) + break; + i++; + } + metaOff += i + 1; i = 0; + + while(data[offset+metaOff+i]) { + if(data[offset+metaOff+i] == 0) + break; + i++; + } + metaOff += i + 1; i = 0; + + //fprintf(stderr, "[%x] Meta\n", offset); + + offset += ((int32_t*)(&data[offset+0x08]))[0]; + } + + // Invalid Chunk + else { + fprintf(stderr, "Sanity test: Chunk type failed.\n"); + //fprintf(stderr, "Type: %u\n", data[offset]); + return NULL; + } + + // Make sure chunks aren't moving us backwards. + if ((int)offset <= (int)prevOffset) { + fprintf(stderr, "Sanity test: forwards offset failed.\n"); + //fprintf(stderr, "Off:%x Fwd:%x\n", offset, prevOffset); + return NULL; + } + prevOffset = offset; + } + + // Sanity test + if (offset != eofOffset) { + fprintf(stderr, "Sanity test: Offset != eof failed.\n"); + //printf("Off: %u EOF: %u\n", offset, eofOffset); + return NULL; + } + + char* texture_data = &data[textureOffset]; + + if(swizzled) { + switch (datFmt) { + case Rgb565: + case Argb1555: + case Argb4444: + texture_data = unswizzle(texture_data, width, height, 16); + break; + case Argb8888: + texture_data = unswizzle(texture_data, width, height, 32); + break; + case Index4: + texture_data = unswizzle(texture_data, width, height, 4); + break; + case Index8: + texture_data = unswizzle(texture_data, width, height, 8); + break; + } + } + + char* newData = NULL; + + char* palData = NULL; + + if(datFmt == Index8) { + // Extract pallete + switch (palFmt) { + case Rgb565: + palData = decode_Rgb565( &data[palleteOffset], 256, 1); + break; + case Argb1555: + palData = decode_Argb1555(&data[palleteOffset], 256, 1); + break; + case Argb4444: + palData = decode_Argb4444(&data[palleteOffset], 256, 1); + break; + case Argb8888: + palData = decode_Argb8888(&data[palleteOffset], 256, 1); + break; + } + } + + if(datFmt == Index4) { + // Extract pallete + switch (palFmt) { + case Rgb565: + palData = decode_Rgb565(&data[palleteOffset], 16, 1); + break; + case Argb1555: + palData = decode_Argb1555(&data[palleteOffset], 16, 1); + break; + case Argb4444: + palData = decode_Argb4444(&data[palleteOffset], 16, 1); + break; + case Argb8888: + palData = decode_Argb8888(&data[palleteOffset], 16, 1); + break; + } + } + + switch (datFmt) { + case Rgb565: + newData = decode_Rgb565(texture_data, width, height); + break; + case Argb1555: + newData = decode_Argb1555(texture_data, width, height); + break; + case Argb4444: + newData = decode_Argb4444(texture_data, width, height); + break; + case Argb8888: + newData = decode_Argb8888(texture_data, width, height); + break; + case Index4: + newData = decode_Index4(texture_data, palData, width, height); + break; + case Index8: + newData = decode_Index8(texture_data, palData, width, height); + break; + case Unknown: + // Invalid format. + return NULL; + } + + OutData* out = (OutData*)calloc(sizeof(OutData), 1); + + out->data = newData; + out->width = width; + out->height = height; + + return out; +} + +uint8_t GIMExport(char* data, size_t len, char* file_out) { + // Convert to a more rational format. + OutData* data_converted = ReadData(data, len); + + if(data_converted == NULL) { + //fprintf(stderr, "GIM data has issues - dying.\n"); + return -1; + } + + // Export to bitmap. + dump_bitmap(file_out, data_converted->data, data_converted->width, data_converted->height); + + return 0; +} + + +uint8_t GIMToBMP(char* file_in, char* file_out) { + FILE* input = fopen(file_in, "rb"); + + // Get length. + fseek(input, 0, SEEK_END); + size_t size = ftell(input); + fseek(input, 0, SEEK_SET); + + // Read in RAW data. + char* input_data = (char*)calloc(sizeof(char), size); + fread(input_data, 1, size, input); + + return GIMExport(input_data, size, file_out); +} + +#ifdef STANDALONE_GIMTOOL + +int main(int argc, char** argv) { + if(argc < 3) { + printf("Usage: %s GIM BMP\n", argv[0]); + return 1; + } + else { + GIMToBMP(argv[1], argv[2]); + return 0; + } +} + +#endif diff --git a/src/gimtool.h b/src/gimtool.h new file mode 100644 index 0000000..0c1437c --- /dev/null +++ b/src/gimtool.h @@ -0,0 +1,71 @@ +#ifndef __GIMTOOL_H__ +#define __GIMTOOL_H__ + +#define Rgb565 0x00 +#define Argb1555 0x01 +#define Argb4444 0x02 +#define Argb8888 0x03 +#define Index4 0x04 +#define Index8 0x05 +#define Unknown 0xFF + +typedef struct __attribute__((packed)) { + char magic1; + char magic2; + uint32_t filesize; + uint32_t reserved; + uint32_t offset; +} bitmap_header; + +typedef struct __attribute__((packed)) { + uint32_t bmpinfohead_size; // Should be 40. + int32_t width; + int32_t height; + uint16_t color_planes; // Should be 1. + uint16_t bpp; // For our purposes, 32bpp + uint32_t compression; // Compression Method + uint32_t image_size; // RAW image size, e.g. w*h*bpp + int32_t ppm_w; // PPM width + int32_t ppm_h; // PPM height + uint32_t colors; // Pallete colors - 0 for 2^n + uint32_t important_col; // Important colors. Set to 0. + uint32_t redmask; // 44 + uint32_t greenmask; // 48 + uint32_t bluemask; // 52 + uint32_t alphamask; // 56 + uint32_t colsptype; // 60 + uint32_t reserved0; // 64 + uint32_t reserved1; // 68 + uint32_t reserved2; // 72 + uint32_t reserved3; // 76 + uint32_t reserved4; // 80 + uint32_t reserved5; // 84 + uint32_t reserved6; // 88 + uint32_t reserved7; // 92 + uint32_t reserved8; // 96 + uint32_t gamma_r; + uint32_t gamma_g; + uint32_t gamma_b; +} bitmapv4_header; + +typedef struct { + int width; + int height; + char* data; +} OutData; + +char* unswizzle(char* data, int width, int height, int bpp); +void dump_bitmap(char* fname, char* data32bpp, int width, int height); + +char* decode_Rgb565(char* data, int width, int height); +char* decode_Argb1555(char* data, int width, int height); +char* decode_Argb4444(char* data, int width, int height); +char* decode_Argb8888(char* data, int width, int height); +char* decode_Index4(char* data, char* palette, int width, int height); +char* decode_Index8(char* data, char* palette, int width, int height); + +OutData* ReadData(char* data, int length); +uint8_t GIMExport(char* data, size_t len, char* file_out); +uint8_t GIMToBMP(char* file_in, char* file_out); + +#endif diff --git a/src/main.c b/src/main.c index 15f2f0e..4ae9bb4 100644 --- a/src/main.c +++ b/src/main.c @@ -183,6 +183,8 @@ main_help () " gimconv command to execute; defaults to 'gimconv'.\n" " --gimconv-flags \n" " Additional flags to pass to gimconv.\n" + " --gimtool-exp (EXPERIMENTAL) Use a new internal GIM->BMP converter\n" + " As fair warning, it currently has issues with some GIMs.\n" " --conv-vag Convert VAG files to WAV.\n" " --decode-vsmx Decode VSMX/JSX files to textual format.\n" " --no-decomp-warn\n" @@ -379,6 +381,7 @@ main_dump (void) RcoDumpGimconvOpts gimconvOpts; uint8_t sConvVag = FALSE; uint8_t sConvVsmx = FALSE; + uint8_t sGimtoolExp = FALSE; int i; @@ -387,6 +390,7 @@ main_dump (void) retrieve_from_opts (TRUE, 13, "--output-txt", "bool", &sTextOutput, "--conv-gim", "text", &gimconvOpts.ext, "--gimconv-cmd", "text", &gimconvOpts.cmd, "--gimconv-flags", "text", &gimconvOpts.extFlags, + "--gimtool-exp", "bool", &sGimtoolExp, "--conv-vag", "bool", &sConvVag, "--decode-vsmx", "bool", &sConvVsmx, "--resdir", "text", &sResDir, "--images", "text", &sResImgDir, "--sounds", "text", &sResSndDir, "--models", "text", &sResMdlDir, "--vsmx", "text", @@ -396,7 +400,7 @@ main_dump (void) #ifndef WIN32 if (gimconvOpts.ext) { warning - ("gimconv support is only available on Windows, as stuff only gets compiled for that platform and Sony doesn't give away source code (duh)."); + ("gimconv support is only available on Windows, as hackers and sony don't seem to like linux tools. Try --gimtool-exp, which isn't complete and chokes on a lot of GIM files. It's better than nothing, right? ;P"); gimconvOpts.ext = NULL; } #endif @@ -452,18 +456,18 @@ main_dump (void) if (!gimconvOpts.ext) arg = NULL; dump_resources (rco->labels, rco->tblImage, RCOXML_TABLE_IMG_FMT, - pathPrefix, arg); + pathPrefix, arg, sGimtoolExp); } if (!sResSndDir || strcmp (sResSndDir, "-")) { RCO_PRINT_PATH (pathPrefix, sResSndDir); dump_resources (rco->labels, rco->tblSound, RCOXML_TABLE_SOUND_FMT, - pathPrefix, (void *) sConvVag); + pathPrefix, (void *) sConvVag, NULL); sndDumped = TRUE; } if (!sResMdlDir || strcmp (sResMdlDir, "-")) { RCO_PRINT_PATH (pathPrefix, sResMdlDir); dump_resources (rco->labels, rco->tblModel, RCOXML_TABLE_MODEL_FMT, - pathPrefix, NULL); + pathPrefix, NULL, NULL); } if (!sResTxtDir || strcmp (sResTxtDir, "-")) { diff --git a/src/rcodump.c b/src/rcodump.c index bfb620f..8ffc1a1 100644 --- a/src/rcodump.c +++ b/src/rcodump.c @@ -28,6 +28,7 @@ #include "rcomain.h" #include "xml.h" #include "strfuncs.h" +#include "gimtool.h" #include "rcodump.h" #include "vaghandler.h" @@ -197,6 +198,109 @@ dump_output_gimconv (char *dest, void *buf, rRCOEntry * entry, void *arg) #endif } +uint8_t +dump_output_gimtool_exp (char *dest, void *buf, rRCOEntry *entry, void* arg) +{ + char tmpName[255]; + static uint32_t successes = 0, failures = 0; + uint8_t ret; + + if (failures <= 5 || successes > 0) { + + get_temp_fname (tmpName, "gim"); + + FILE *fp = fopen (tmpName, "wb"); + + if (!fp) + return FALSE; + + // try to apply gim patch + if (entry->srcLenUnpacked > 0x28) { + // dirty code - we'll leave here, since the internal converter will have + // nicer code :P + uint32_t *i32; + uint8_t es = FALSE; + + i32 = (uint32_t *) buf; + if (*i32 == 0x4D49472E) + es = TRUE; // .GIM + + if (*i32 == 0x2E47494D || *i32 == 0x4D49472E) { + uint16_t i, i2; + + i = *(uint16_t *) ((char *) buf + 0x10); + i2 = *(uint16_t *) ((char *) buf + 0x20); + if (es) { + i = ENDIAN_SWAP (i); + i2 = ENDIAN_SWAP (i2); + } + if (i == 2 && i2 == 3) { + uint32_t sz = *(uint32_t *) ((char *) buf + 0x14), sz2; + + i32 = (uint32_t *) ((char *) buf + 0x24); + sz2 = *i32; + if (es) { + sz = ENDIAN_SWAP (sz); + sz2 = ENDIAN_SWAP (sz2); + } + if (sz - 0x10 != sz2) { + info ("Note: Applied GIM patch when using Gimtool-exp to dump '%s'.", + dest); + sz2 = sz - 0x10; + if (es) + sz2 = ENDIAN_SWAP (sz2); + *i32 = sz2; + } + } + } + } + + filewrite (fp, buf, entry->srcLenUnpacked); + fclose (fp); + + int tmppos; + for(tmppos=0; tmppos < strlen(dest); tmppos++) { + if(dest[tmppos] == '.' && (strlen(dest) - tmppos) > 3 && dest[tmppos+1] == 'g' && dest[tmppos+2] == 'i' && dest[tmppos+3] == 'm') { + dest[tmppos+1] = 'b'; + dest[tmppos+2] = 'm'; + dest[tmppos+3] = 'p'; + + break; + } + } + + ret = ! GIMToBMP(tmpName, dest); + + remove (tmpName); + + if (!ret) { + warning + ("gimtool-exp failed to process GIM. Resource being dumped as GIM instead."); + failures++; + if (failures > 5 && successes == 0) { + warning + ("Gimtool-exp failed too many times without success - disabling Gimtool-exp (GIMs will not be converted)."); + } + } + } else + ret = FALSE; + + if (!ret) { + char tmpDest[260]; + + strcpy (tmpDest, dest); + char *dot = strrchr (tmpDest, '.'); + + if (!dot) + dot = tmpDest + strlen (tmpDest); + strcpy (dot, ".gim"); + + return dump_output_data (tmpDest, buf, entry, NULL); + } else + successes++; + return ret; +} + uint8_t dump_output_vsmxdec (char *dest, void *buf, rRCOEntry * entry, void *arg) { @@ -220,7 +324,7 @@ dump_output_vsmxdec (char *dest, void *buf, rRCOEntry * entry, void *arg) void dump_resources (char *labels, rRCOEntry * parent, const RcoTableMap extMap, - char *pathPrefix, void *outputFilterArg) + char *pathPrefix, void *outputFilterArg, int optional) { // TODO: remove text crap from this function if (!parent || !parent->numSubentries) @@ -278,9 +382,13 @@ dump_resources (char *labels, rRCOEntry * parent, const RcoTableMap extMap, of = dump_output_wav; else if (entry->id == RCO_TABLE_IMG && ((rRCOImgModelEntry *) entry->extra)->format == RCO_IMG_GIM) - of = dump_output_gimconv; + of = dump_output_gimconv; } + if (optional == 1 && entry->id == RCO_TABLE_IMG && + ((rRCOImgModelEntry *) entry->extra)->format == RCO_IMG_GIM) + of = dump_output_gimtool_exp; + info ("Dumping resource '%s'...", label); if (entry->id == RCO_TABLE_SOUND && !outputFilterArg) { diff --git a/src/rcodump.h b/src/rcodump.h index e387ee9..3346184 100644 --- a/src/rcodump.h +++ b/src/rcodump.h @@ -21,7 +21,7 @@ typedef struct { uint8_t dump_resource (char *dest, rRCOEntry * entry, OutputDumpFunc outputfunc, void *outputfuncArg); void dump_resources (char *labels, rRCOEntry * parent, const RcoTableMap extMap, - char *pathPrefix, void *outputFilterArg); + char *pathPrefix, void *outputFilterArg, int optional); void dump_text_resources (char *labels, rRCOEntry * parent, uint8_t writeHeader, char *pathPrefix, uint8_t bWriteXML); uint8_t dump_output_data (char *dest, void *buf, rRCOEntry * entry, void *arg);