From 4d98d55b0e2cb43532d1a65d4df5868b4ec77e57 Mon Sep 17 00:00:00 2001 From: Adam <32522043+GHFear@users.noreply.github.com> Date: Tue, 26 Dec 2023 22:04:24 +0100 Subject: [PATCH] Code cleanup / speedup and preparing for LZX. --- RW4ArchiveTool/Archives/Compression/lzx/lzx.c | 864 ++++++++++++++ RW4ArchiveTool/Archives/Compression/lzx/lzx.h | 60 + RW4ArchiveTool/Archives/Packers/big4_packer.h | 232 ++-- RW4ArchiveTool/Archives/Parsers/big4_parser.h | 456 ++++---- .../Archives/Parsers/big_eb_parser.h | 1025 ++++++++--------- RW4ArchiveTool/Archives/Tools/EAHashes.h | 34 +- RW4ArchiveTool/Archives/Tools/IoTools.h | 216 ++-- .../Archives/Tools/big_endian_tools.h | 126 +- RW4ArchiveTool/RW4ArchiveTool.vcxproj | 10 +- RW4ArchiveTool/RW4ArchiveTool.vcxproj.filters | 2 + RW4ArchiveTool/Resources/RW4ArchiveTool.aps | Bin 101356 -> 101356 bytes RW4ArchiveTool/Resources/RW4ArchiveTool.rc | Bin 11500 -> 11500 bytes .../Tools/Program/ProgramHandlers.h | 975 ++++++++-------- RW4ArchiveTool/Unspecified/Global.h | 2 +- RW4ArchiveTool/rw4_archive_tool.cpp | 983 ++++++++-------- 15 files changed, 3000 insertions(+), 1985 deletions(-) create mode 100644 RW4ArchiveTool/Archives/Compression/lzx/lzx.c create mode 100644 RW4ArchiveTool/Archives/Compression/lzx/lzx.h diff --git a/RW4ArchiveTool/Archives/Compression/lzx/lzx.c b/RW4ArchiveTool/Archives/Compression/lzx/lzx.c new file mode 100644 index 0000000..bf31dd7 --- /dev/null +++ b/RW4ArchiveTool/Archives/Compression/lzx/lzx.c @@ -0,0 +1,864 @@ +/*************************************************************************** + * lzx.c - LZX decompression routines * + * ------------------- * + * * + * maintainer: Jed Wing * + * source: modified lzx.c from cabextract v0.5 * + * notes: This file was taken from cabextract v0.5, which was, * + * itself, a modified version of the lzx decompression code * + * from unlzx. * + * * + * platforms: In its current incarnation, this file has been tested on * + * two different Linux platforms (one, redhat-based, with a * + * 2.1.2 glibc and gcc 2.95.x, and the other, Debian, with * + * 2.2.4 glibc and both gcc 2.95.4 and gcc 3.0.2). Both were * + * Intel x86 compatible machines. * + ***************************************************************************/ + + /*************************************************************************** + * * + * 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 of the License, or * + * (at your option) any later version. Note that an exemption to this * + * license has been granted by Stuart Caie for the purposes of * + * distribution with chmlib. This does not, to the best of my * + * knowledge, constitute a change in the license of this (the LZX) code * + * in general. * + * * + ***************************************************************************/ + +#include "lzx.h" +#include +#include +#include +#include + + /* some constants defined by the LZX specification */ +#define LZX_MIN_MATCH 2 +/* #define LZX_MAX_MATCH 257 */ +#define LZX_NUM_CHARS 256 +#define LZX_BLOCKTYPE_INVALID 0 /* also blocktypes 4-7 invalid */ +#define LZX_BLOCKTYPE_VERBATIM 1 +#define LZX_BLOCKTYPE_ALIGNED 2 +#define LZX_BLOCKTYPE_UNCOMPRESSED 3 +#define LZX_PRETREE_NUM_ELEMENTS 20 +#define LZX_ALIGNED_NUM_ELEMENTS 8 /* aligned offset tree #elements */ +#define LZX_NUM_PRIMARY_LENGTHS 7 /* this one missing from spec! */ +#define LZX_NUM_SECONDARY_LENGTHS 249 /* length tree #elements */ + +/* LZX huffman defines: tweak tablebits as desired */ +#define LZX_PRETREE_MAXSYMBOLS LZX_PRETREE_NUM_ELEMENTS +#define LZX_PRETREE_TABLEBITS 6 +#define LZX_MAINTREE_MAXSYMBOLS (LZX_NUM_CHARS + 50 * 8) +#define LZX_MAINTREE_TABLEBITS 12 +#define LZX_LENGTH_MAXSYMBOLS (LZX_NUM_SECONDARY_LENGTHS + 1) +#define LZX_LENGTH_TABLEBITS 12 +#define LZX_ALIGNED_MAXSYMBOLS LZX_ALIGNED_NUM_ELEMENTS +#define LZX_ALIGNED_TABLEBITS 7 + +#define LZX_LENTABLE_SAFETY 64 /* we allow length table decoding overruns */ + +#define LZX_DECLARE_TABLE(tbl) \ + uint16_t tbl##_table[(1 << LZX_##tbl##_TABLEBITS) + (LZX_##tbl##_MAXSYMBOLS << 1)]; \ + uint8_t tbl##_len[LZX_##tbl##_MAXSYMBOLS + LZX_LENTABLE_SAFETY] + +struct lzx_state { + uint8_t* window; /* the actual decoding window */ + uint32_t window_size; /* window size (32Kb through 2Mb) */ + uint32_t actual_size; /* window size when it was first allocated */ + uint32_t window_posn; /* current offset within the window */ + uint32_t R0, R1, R2; /* for the LRU offset system */ + uint16_t main_elements; /* number of main tree elements */ + int header_read; /* have we started decoding at all yet? */ + uint16_t block_type; /* type of this block */ + uint32_t block_length; /* uncompressed length of this block */ + uint32_t block_remaining; /* uncompressed bytes still left to decode */ + uint32_t frames_read; /* the number of CFDATA blocks processed */ + int32_t intel_filesize; /* magic header value used for transform */ + int32_t intel_curpos; /* current offset in transform space */ + int intel_started; /* have we seen any translatable data yet? */ + + LZX_DECLARE_TABLE(PRETREE); + LZX_DECLARE_TABLE(MAINTREE); + LZX_DECLARE_TABLE(LENGTH); + LZX_DECLARE_TABLE(ALIGNED); +}; + +/* LZX decruncher */ + +/* Microsoft's LZX document and their implementation of the + * com.ms.util.cab Java package do not concur. + * + * In the LZX document, there is a table showing the correlation between + * window size and the number of position slots. It states that the 1MB + * window = 40 slots and the 2MB window = 42 slots. In the implementation, + * 1MB = 42 slots, 2MB = 50 slots. The actual calculation is 'find the + * first slot whose position base is equal to or more than the required + * window size'. This would explain why other tables in the document refer + * to 50 slots rather than 42. + * + * The constant NUM_PRIMARY_LENGTHS used in the decompression pseudocode + * is not defined in the specification. + * + * The LZX document does not state the uncompressed block has an + * uncompressed length field. Where does this length field come from, so + * we can know how large the block is? The implementation has it as the 24 + * bits following after the 3 blocktype bits, before the alignment + * padding. + * + * The LZX document states that aligned offset blocks have their aligned + * offset huffman tree AFTER the main and length trees. The implementation + * suggests that the aligned offset tree is BEFORE the main and length + * trees. + * + * The LZX document decoding algorithm states that, in an aligned offset + * block, if an extra_bits value is 1, 2 or 3, then that number of bits + * should be read and the result added to the match offset. This is + * correct for 1 and 2, but not 3, where just a huffman symbol (using the + * aligned tree) should be read. + * + * Regarding the E8 preprocessing, the LZX document states 'No translation + * may be performed on the last 6 bytes of the input block'. This is + * correct. However, the pseudocode provided checks for the *E8 leader* + * up to the last 6 bytes. If the leader appears between -10 and -7 bytes + * from the end, this would cause the next four bytes to be modified, at + * least one of which would be in the last 6 bytes, which is not allowed + * according to the spec. + * + * The specification states that the huffman trees must always contain at + * least one element. However, many CAB files contain blocks where the + * length tree is completely empty (because there are no matches), and + * this is expected to succeed. + */ + + /* LZX uses what it calls 'position slots' to represent match offsets. + * What this means is that a small 'position slot' number and a small + * offset from that slot are encoded instead of one large offset for + * every match. + * - position_base is an index to the position slot bases + * - extra_bits states how many bits of offset-from-base data is needed. + */ +static const uint8_t extra_bits[51] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, + 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, + 12, 12, 13, 13, 14, 14, 15, 15, 16, 16, 17, 17, 17, + 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17 }; + +static const uint32_t position_base[51] = { + 0, 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, + 48, 64, 96, 128, 192, 256, 384, 512, 768, 1024, 1536, + 2048, 3072, 4096, 6144, 8192, 12288, 16384, 24576, 32768, 49152, 65536, + 98304, 131072, 196608, 262144, 393216, 524288, 655360, 786432, 917504, 1048576, 1179648, + 1310720, 1441792, 1572864, 1703936, 1835008, 1966080, 2097152 }; + +struct lzx_state* lzx_init(int window) { + struct lzx_state* pState = NULL; + uint32_t wndsize = 1 << window; + int i, posn_slots; + + /* LZX supports window sizes of 2^15 (32Kb) through 2^21 (2Mb) */ + /* if a previously allocated window is big enough, keep it */ + if (window < 15 || window > 21) + return NULL; + + /* allocate state and associated window */ + pState = (struct lzx_state*)malloc(sizeof(struct lzx_state)); + if (!pState || !(pState->window = (uint8_t*)malloc(wndsize))) { + free(pState); + return NULL; + } + pState->actual_size = wndsize; + pState->window_size = wndsize; + + /* calculate required position slots */ + if (window == 20) + posn_slots = 42; + else if (window == 21) + posn_slots = 50; + else + posn_slots = window << 1; + + /** alternatively **/ + /* posn_slots=i=0; while (i < wndsize) i += 1 << extra_bits[posn_slots++]; */ + + /* initialize other state */ + pState->R0 = pState->R1 = pState->R2 = 1; + pState->main_elements = LZX_NUM_CHARS + (posn_slots << 3); + pState->header_read = 0; + pState->frames_read = 0; + pState->block_remaining = 0; + pState->block_type = LZX_BLOCKTYPE_INVALID; + pState->intel_curpos = 0; + pState->intel_started = 0; + pState->window_posn = 0; + + /* initialise tables to 0 (because deltas will be applied to them) */ + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS; i++) + pState->MAINTREE_len[i] = 0; + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS; i++) + pState->LENGTH_len[i] = 0; + + return pState; +} + +void lzx_teardown(struct lzx_state* pState) { + if (pState) { + if (pState->window) + free(pState->window); + free(pState); + } +} + +void lzx_reset(struct lzx_state* pState) { + int i; + + pState->R0 = pState->R1 = pState->R2 = 1; + pState->header_read = 0; + pState->frames_read = 0; + pState->block_remaining = 0; + pState->block_type = LZX_BLOCKTYPE_INVALID; + pState->intel_curpos = 0; + pState->intel_started = 0; + pState->window_posn = 0; + + for (i = 0; i < LZX_MAINTREE_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) { + pState->MAINTREE_len[i] = 0; + } + + for (i = 0; i < LZX_LENGTH_MAXSYMBOLS + LZX_LENTABLE_SAFETY; i++) { + pState->LENGTH_len[i] = 0; + } +} + +/* Bitstream reading macros: + * + * INIT_BITSTREAM should be used first to set up the system + * READ_BITS(var,n) takes N bits from the buffer and puts them in var + * + * ENSURE_BITS(n) ensures there are at least N bits in the bit buffer + * PEEK_BITS(n) extracts (without removing) N bits from the bit buffer + * REMOVE_BITS(n) removes N bits from the bit buffer + * + * These bit access routines work by using the area beyond the MSB and the + * LSB as a free source of zeroes. This avoids having to mask any bits. + * So we have to know the bit width of the bitbuffer variable. This is + * sizeof(uint32_t) * 8, also defined as uint32_t_BITS + */ + + /* number of bits in uint32_t. Note: This must be at multiple of 16, and at + * least 32 for the bitbuffer code to work (ie, it must be able to ensure + * up to 17 bits - that's adding 16 bits when there's one bit left, or + * adding 32 bits when there are no bits left. The code should work fine + * for machines where uint32_t >= 32 bits. + */ +#define uint32_t_BITS (sizeof(uint32_t) << 3) + +#define INIT_BITSTREAM \ + do { \ + bitsleft = 0; \ + bitbuf = 0; \ + } while (0) + +#define ENSURE_BITS(n) \ + while (bitsleft < (n)) { \ + bitbuf |= ((inpos[1] << 8) | inpos[0]) << (uint32_t_BITS - 16 - bitsleft); \ + bitsleft += 16; \ + inpos += 2; \ + } + +#define PEEK_BITS(n) (bitbuf >> (uint32_t_BITS - (n))) +#define REMOVE_BITS(n) ((bitbuf <<= (n)), (bitsleft -= (n))) + +#define READ_BITS(v, n) \ + do { \ + ENSURE_BITS(n); \ + (v) = PEEK_BITS(n); \ + REMOVE_BITS(n); \ + } while (0) + + /* Huffman macros */ + +#define TABLEBITS(tbl) (LZX_##tbl##_TABLEBITS) +#define MAXSYMBOLS(tbl) (LZX_##tbl##_MAXSYMBOLS) +#define SYMTABLE(tbl) (pState->tbl##_table) +#define LENTABLE(tbl) (pState->tbl##_len) + +/* BUILD_TABLE(tablename) builds a huffman lookup table from code lengths. + * In reality, it just calls make_decode_table() with the appropriate + * values - they're all fixed by some #defines anyway, so there's no point + * writing each call out in full by hand. + */ +#define BUILD_TABLE(tbl) \ + if (make_decode_table(MAXSYMBOLS(tbl), TABLEBITS(tbl), LENTABLE(tbl), SYMTABLE(tbl))) { \ + return DECR_ILLEGALDATA; \ + } + + /* READ_HUFFSYM(tablename, var) decodes one huffman symbol from the + * bitstream using the stated table and puts it in var. + */ +#define READ_HUFFSYM(tbl, var) \ + do { \ + ENSURE_BITS(16); \ + hufftbl = SYMTABLE(tbl); \ + if ((i = hufftbl[PEEK_BITS(TABLEBITS(tbl))]) >= MAXSYMBOLS(tbl)) { \ + j = 1 << (uint32_t_BITS - TABLEBITS(tbl)); \ + do { \ + j >>= 1; \ + i <<= 1; \ + i |= (bitbuf & j) ? 1 : 0; \ + if (!j) { \ + return DECR_ILLEGALDATA; \ + } \ + } while ((i = hufftbl[i]) >= MAXSYMBOLS(tbl)); \ + } \ + j = LENTABLE(tbl)[(var) = i]; \ + REMOVE_BITS(j); \ + } while (0) + + /* READ_LENGTHS(tablename, first, last) reads in code lengths for symbols + * first to last in the given table. The code lengths are stored in their + * own special LZX way. + */ +#define READ_LENGTHS(tbl, first, last) \ + do { \ + lb.bb = bitbuf; \ + lb.bl = bitsleft; \ + lb.ip = inpos; \ + if (lzx_read_lens(pState, LENTABLE(tbl), (first), (last), &lb)) { \ + return DECR_ILLEGALDATA; \ + } \ + bitbuf = lb.bb; \ + bitsleft = lb.bl; \ + inpos = lb.ip; \ + } while (0) + + /* make_decode_table(nsyms, nbits, length[], table[]) + * + * This function was coded by David Tritscher. It builds a fast huffman + * decoding table out of just a canonical huffman code lengths table. + * + * nsyms = total number of symbols in this huffman tree. + * nbits = any symbols with a code length of nbits or less can be decoded + * in one lookup of the table. + * length = A table to get code lengths from [0 to syms-1] + * table = The table to fill up with decoded symbols and pointers. + * + * Returns 0 for OK or 1 for error + */ +static int make_decode_table(uint32_t nsyms, uint32_t nbits, uint8_t* length, uint16_t* table) { + uint16_t sym; + uint32_t leaf; + uint8_t bit_num = 1; + uint32_t fill; + uint32_t pos = 0; /* the current position in the decode table */ + uint32_t table_mask = 1 << nbits; + uint32_t bit_mask = table_mask >> 1; /* don't do 0 length codes */ + uint32_t next_symbol = bit_mask; /* base of allocation for long codes */ + + /* fill entries for codes short enough for a direct mapping */ + while (bit_num <= nbits) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) { + continue; + } + leaf = pos; + + if ((pos += bit_mask) > table_mask) + return 1; /* table overrun */ + + /* fill all possible lookups of this symbol with the symbol itself */ + fill = bit_mask; + while (fill-- > 0) { + table[leaf++] = sym; + } + } + bit_mask >>= 1; + bit_num++; + } + + /* if there are any codes longer than nbits */ + if (pos != table_mask) { + /* clear the remainder of the table */ + for (sym = pos; sym < table_mask; sym++) { + table[sym] = 0; + } + + /* give ourselves room for codes to grow by up to 16 more bits */ + pos <<= 16; + table_mask <<= 16; + bit_mask = 1 << 15; + + while (bit_num <= 16) { + for (sym = 0; sym < nsyms; sym++) { + if (length[sym] != bit_num) { + continue; + } + leaf = pos >> 16; + for (fill = 0; fill < bit_num - nbits; fill++) { + /* if this path hasn't been taken yet, 'allocate' two entries */ + if (table[leaf] == 0) { + table[(next_symbol << 1)] = 0; + table[(next_symbol << 1) + 1] = 0; + table[leaf] = next_symbol++; + } + /* follow the path and select either left or right for next bit */ + leaf = table[leaf] << 1; + if ((pos >> (15 - fill)) & 1) + leaf++; + } + table[leaf] = sym; + + if ((pos += bit_mask) > table_mask) + return 1; /* table overflow */ + } + bit_mask >>= 1; + bit_num++; + } + } + + /* full table? */ + if (pos == table_mask) + return 0; + + /* either erroneous table, or all elements are 0 - let's find out. */ + for (sym = 0; sym < nsyms; sym++) { + if (length[sym]) + return 1; + } + return 0; +} + +struct lzx_bits { + uint32_t bb; + int bl; + uint8_t* ip; +}; + +static int lzx_read_lens(struct lzx_state* pState, uint8_t* lens, uint32_t first, uint32_t last, + struct lzx_bits* lb) { + uint32_t i, j, x, y; + int z; + + uint32_t bitbuf = lb->bb; + int bitsleft = lb->bl; + uint8_t* inpos = lb->ip; + uint16_t* hufftbl; + + for (x = 0; x < 20; x++) { + READ_BITS(y, 4); + LENTABLE(PRETREE)[x] = y; + } + BUILD_TABLE(PRETREE); + + for (x = first; x < last;) { + READ_HUFFSYM(PRETREE, z); + if (z == 17) { + READ_BITS(y, 4); + y += 4; + while (y--) + lens[x++] = 0; + } + else if (z == 18) { + READ_BITS(y, 5); + y += 20; + while (y--) + lens[x++] = 0; + } + else if (z == 19) { + READ_BITS(y, 1); + y += 4; + READ_HUFFSYM(PRETREE, z); + z = lens[x] - z; + if (z < 0) + z += 17; + while (y--) + lens[x++] = z; + } + else { + z = lens[x] - z; + if (z < 0) + z += 17; + lens[x++] = z; + } + } + + lb->bb = bitbuf; + lb->bl = bitsleft; + lb->ip = inpos; + return 0; +} + +int lzx_decompress(struct lzx_state* pState, unsigned char* inpos, unsigned char* outpos, int inlen, + int outlen) { + uint8_t* endinp = inpos + inlen; + uint8_t* window = pState->window; + uint8_t* runsrc, * rundest; + uint16_t* hufftbl; /* used in READ_HUFFSYM macro as chosen decoding table */ + + uint32_t window_posn = pState->window_posn; + uint32_t window_size = pState->window_size; + uint32_t R0 = pState->R0; + uint32_t R1 = pState->R1; + uint32_t R2 = pState->R2; + + uint32_t bitbuf; + int bitsleft; + uint32_t match_offset, i, j, k; /* ijk used in READ_HUFFSYM macro */ + struct lzx_bits lb; /* used in READ_LENGTHS macro */ + + int togo = outlen, this_run, main_element, aligned_bits; + int match_length, length_footer, extra, verbatim_bits; + + INIT_BITSTREAM; + + /* read header if necessary */ + if (!pState->header_read) { + i = j = 0; + READ_BITS(k, 1); + if (k) { + READ_BITS(i, 16); + READ_BITS(j, 16); + } + pState->intel_filesize = (i << 16) | j; /* or 0 if not encoded */ + pState->header_read = 1; + } + + /* main decoding loop */ + while (togo > 0) { + /* last block finished, new block expected */ + if (pState->block_remaining == 0) { + if (pState->block_type == LZX_BLOCKTYPE_UNCOMPRESSED) { + if (pState->block_length & 1) + inpos++; /* realign bitstream to word */ + INIT_BITSTREAM; + } + + READ_BITS(pState->block_type, 3); + READ_BITS(i, 16); + READ_BITS(j, 8); + pState->block_remaining = pState->block_length = (i << 8) | j; + + switch (pState->block_type) { + case LZX_BLOCKTYPE_ALIGNED: + for (i = 0; i < 8; i++) { + READ_BITS(j, 3); + LENTABLE(ALIGNED)[i] = j; + } + BUILD_TABLE(ALIGNED); + /* rest of aligned header is same as verbatim */ + + case LZX_BLOCKTYPE_VERBATIM: + READ_LENGTHS(MAINTREE, 0, 256); + READ_LENGTHS(MAINTREE, 256, pState->main_elements); + BUILD_TABLE(MAINTREE); + if (LENTABLE(MAINTREE)[0xE8] != 0) + pState->intel_started = 1; + + READ_LENGTHS(LENGTH, 0, LZX_NUM_SECONDARY_LENGTHS); + BUILD_TABLE(LENGTH); + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + pState->intel_started = 1; /* because we can't assume otherwise */ + ENSURE_BITS(16); /* get up to 16 pad bits into the buffer */ + if (bitsleft > 16) + inpos -= 2; /* and align the bitstream! */ + R0 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + R1 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + R2 = inpos[0] | (inpos[1] << 8) | (inpos[2] << 16) | (inpos[3] << 24); + inpos += 4; + break; + + default: + return DECR_ILLEGALDATA; + } + } + + /* buffer exhaustion check */ + if (inpos > endinp) { + /* it's possible to have a file where the next run is less than + * 16 bits in size. In this case, the READ_HUFFSYM() macro used + * in building the tables will exhaust the buffer, so we should + * allow for this, but not allow those accidentally read bits to + * be used (so we check that there are at least 16 bits + * remaining - in this boundary case they aren't really part of + * the compressed data) + */ + if (inpos > (endinp + 2) || bitsleft < 16) + return DECR_ILLEGALDATA; + } + + while ((this_run = pState->block_remaining) > 0 && togo > 0) { + if (this_run > togo) + this_run = togo; + togo -= this_run; + pState->block_remaining -= this_run; + + /* apply 2^x-1 mask */ + window_posn &= window_size - 1; + /* runs can't straddle the window wraparound */ + if ((window_posn + this_run) > window_size) + return DECR_DATAFORMAT; + + switch (pState->block_type) { + case LZX_BLOCKTYPE_VERBATIM: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + if (match_offset != 3) { + extra = extra_bits[match_offset]; + READ_BITS(verbatim_bits, extra); + match_offset = position_base[match_offset] - 2 + verbatim_bits; + } + else { + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; + R1 = R0; + R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; + R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; + R0 = match_offset; + } + + rundest = window + window_posn; + runsrc = rundest - match_offset; + window_posn += match_length; + if (window_posn > window_size) + return DECR_ILLEGALDATA; + this_run -= match_length; + + /* copy any wrapped around source data */ + while ((runsrc < window) && (match_length-- > 0)) { + *rundest++ = *(runsrc + window_size); + runsrc++; + } + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_ALIGNED: + while (this_run > 0) { + READ_HUFFSYM(MAINTREE, main_element); + + if (main_element < LZX_NUM_CHARS) { + /* literal: 0 to LZX_NUM_CHARS-1 */ + window[window_posn++] = main_element; + this_run--; + } + else { + /* match: LZX_NUM_CHARS + ((slot<<3) | length_header (3 bits)) */ + main_element -= LZX_NUM_CHARS; + + match_length = main_element & LZX_NUM_PRIMARY_LENGTHS; + if (match_length == LZX_NUM_PRIMARY_LENGTHS) { + READ_HUFFSYM(LENGTH, length_footer); + match_length += length_footer; + } + match_length += LZX_MIN_MATCH; + + match_offset = main_element >> 3; + + if (match_offset > 2) { + /* not repeated offset */ + extra = extra_bits[match_offset]; + match_offset = position_base[match_offset] - 2; + if (extra > 3) { + /* verbatim and aligned bits */ + extra -= 3; + READ_BITS(verbatim_bits, extra); + match_offset += (verbatim_bits << 3); + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra == 3) { + /* aligned bits only */ + READ_HUFFSYM(ALIGNED, aligned_bits); + match_offset += aligned_bits; + } + else if (extra > 0) { /* extra==1, extra==2 */ + /* verbatim bits only */ + READ_BITS(verbatim_bits, extra); + match_offset += (uint32_t)verbatim_bits; + } + else /* extra == 0 */ { + /* ??? */ + match_offset = 1; + } + + /* update repeated offset LRU queue */ + R2 = R1; + R1 = R0; + R0 = match_offset; + } + else if (match_offset == 0) { + match_offset = R0; + } + else if (match_offset == 1) { + match_offset = R1; + R1 = R0; + R0 = match_offset; + } + else /* match_offset == 2 */ { + match_offset = R2; + R2 = R0; + R0 = match_offset; + } + + rundest = window + window_posn; + runsrc = rundest - match_offset; + window_posn += (uint32_t)match_length; + if (window_posn > window_size) + return DECR_ILLEGALDATA; + this_run -= match_length; + + /* copy any wrapped around source data */ + while ((runsrc < window) && (match_length-- > 0)) { + *rundest++ = *(runsrc + window_size); + runsrc++; + } + /* copy match data - no worries about destination wraps */ + while (match_length-- > 0) + *rundest++ = *runsrc++; + } + } + break; + + case LZX_BLOCKTYPE_UNCOMPRESSED: + if ((inpos + this_run) > endinp) + return DECR_ILLEGALDATA; + memcpy(window + window_posn, inpos, (size_t)this_run); + inpos += this_run; + window_posn += (uint32_t)this_run; + break; + + default: + return DECR_ILLEGALDATA; /* might as well */ + } + } + } + + if (togo != 0) + return DECR_ILLEGALDATA; + memcpy(outpos, window + ((!window_posn) ? window_size : window_posn) - outlen, (size_t)outlen); + + pState->window_posn = window_posn; + pState->R0 = R0; + pState->R1 = R1; + pState->R2 = R2; + + /* intel E8 decoding */ + if ((pState->frames_read++ < 32768) && pState->intel_filesize != 0) { + if (outlen <= 6 || !pState->intel_started) { + pState->intel_curpos += outlen; + } + else { + uint8_t* data = outpos; + uint8_t* dataend = data + outlen - 10; + int32_t curpos = pState->intel_curpos; + int32_t filesize = pState->intel_filesize; + int32_t abs_off, rel_off; + + pState->intel_curpos = curpos + outlen; + + while (data < dataend) { + if (*data++ != 0xE8) { + curpos++; + continue; + } + abs_off = data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); + if ((abs_off >= -curpos) && (abs_off < filesize)) { + rel_off = (abs_off >= 0) ? abs_off - curpos : abs_off + filesize; + data[0] = (uint8_t)rel_off; + data[1] = (uint8_t)(rel_off >> 8); + data[2] = (uint8_t)(rel_off >> 16); + data[3] = (uint8_t)(rel_off >> 24); + } + data += 4; + curpos += 5; + } + } + } + return DECR_OK; +} + +#ifdef LZX_CHM_TESTDRIVER +int main(int c, char** v) { + FILE* fin, * fout; + struct lzx_state state; + uint8_t ibuf[16384]; + uint8_t obuf[32768]; + int ilen, olen; + int status; + int i; + int count = 0; + int w = atoi(v[1]); + lzx_init(&state, w); + fout = fopen(v[2], "wb"); + for (i = 3; i < c; i++) { + fin = fopen(v[i], "rb"); + ilen = fread(ibuf, 1, 16384, fin); + status = lzx_decompress(&state, ibuf, obuf, ilen, 32768); + switch (status) { + case DECR_OK: + printf("ok\n"); + fwrite(obuf, 1, 32768, fout); + break; + case DECR_DATAFORMAT: + printf("bad format\n"); + break; + case DECR_ILLEGALDATA: + printf("illegal data\n"); + break; + case DECR_NOMEMORY: + printf("no memory\n"); + break; + default: + break; + } + fclose(fin); + if (++count == 2) { + count = 0; + lzx_reset(&state); + } + } + fclose(fout); +} +#endif \ No newline at end of file diff --git a/RW4ArchiveTool/Archives/Compression/lzx/lzx.h b/RW4ArchiveTool/Archives/Compression/lzx/lzx.h new file mode 100644 index 0000000..bc353e0 --- /dev/null +++ b/RW4ArchiveTool/Archives/Compression/lzx/lzx.h @@ -0,0 +1,60 @@ +#pragma once + +/*************************************************************************** +* lzx.h - LZX decompression routines * +* ------------------- * +* * +* maintainer: Jed Wing * +* source: modified lzx.c from cabextract v0.5 * +* notes: This file was taken from cabextract v0.5, which was, * +* itself, a modified version of the lzx decompression code * +* from unlzx. * +***************************************************************************/ + +/*************************************************************************** + * * + * 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 of the License, or * + * (at your option) any later version. Note that an exemption to this * + * license has been granted by Stuart Caie for the purposes of * + * distribution with chmlib. This does not, to the best of my * + * knowledge, constitute a change in the license of this (the LZX) code * + * in general. * + * * + ***************************************************************************/ + +#ifndef INCLUDED_LZX_H +#define INCLUDED_LZX_H + +#ifdef __cplusplus +extern "C" { +#endif + + /* return codes */ +#define DECR_OK (0) +#define DECR_DATAFORMAT (1) +#define DECR_ILLEGALDATA (2) +#define DECR_NOMEMORY (3) + +/* opaque state structure */ + struct lzx_state; + + /* create an lzx state object */ + struct lzx_state* lzx_init(int window); + + /* destroy an lzx state object */ + void lzx_teardown(struct lzx_state* pState); + + /* reset an lzx stream */ + void lzx_reset(struct lzx_state* pState); + + /* decompress an LZX compressed block */ + int lzx_decompress(struct lzx_state* pState, unsigned char* inpos, unsigned char* outpos, int inlen, + int outlen); + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_LZX_H */ \ No newline at end of file diff --git a/RW4ArchiveTool/Archives/Packers/big4_packer.h b/RW4ArchiveTool/Archives/Packers/big4_packer.h index 147dd5d..747592f 100644 --- a/RW4ArchiveTool/Archives/Packers/big4_packer.h +++ b/RW4ArchiveTool/Archives/Packers/big4_packer.h @@ -33,7 +33,7 @@ namespace big4 uint32_t header_size; }; - struct BH_TOC_Hash32 + struct BH_TOC_Hash32 // Use this for 32 bit namehashes. { uint32_t offset; uint32_t size; @@ -41,7 +41,7 @@ namespace big4 uint32_t hash; }; - struct BH_TOC_Hash64 + struct BH_TOC_Hash64 // Use this for 64 bit namehashes. { uint32_t offset; uint32_t size; @@ -66,8 +66,7 @@ namespace big4 const char* Big4_Magic_Setting[2] = { "BIG4", "BIGF" }; - uint64_t get_big4_toc_index_size(const std::wstring& input_wstring) - { + uint64_t get_big4_toc_index_size(const std::wstring& input_wstring) { return 8 + input_wstring.length() + 1; } @@ -78,14 +77,15 @@ namespace big4 size_t current_offset = outfile.tellp(); if (current_offset % alignment == 0) { - // No pćadding needed. - return false; + // No padding needed. + return true; } else { size_t padding_size = alignment - (current_offset % alignment); for (size_t i = 0; i < padding_size; i++) { outfile.write(reinterpret_cast(&padding), 1); + if (!outfile.good()) { return false; } } return true; } @@ -106,8 +106,7 @@ namespace big4 uint32_t footer_size = 16; uint32_t file_count = Big4Toc_vector.size(); - if (IsHash64 == true) - { + if (IsHash64 == true) { toc_size = 20 * Big4Toc_vector.size(); } @@ -119,8 +118,7 @@ namespace big4 BHHeader.header_size = BigToLittleUINT(BHHeader.size); // Set header size. (Big endian) outfile.write(reinterpret_cast(&BHHeader), sizeof(BHHeader)); // Write header - if (IsHash64 == true) - { + if (IsHash64 == true) { // BH TOC 64 BH_TOC_Hash64 BH_TOC = {}; for (size_t i = 0; i < Big4Toc_vector.size(); i++) @@ -137,8 +135,7 @@ namespace big4 outfile.write(reinterpret_cast(&BH_TOC.hash), 8); } } - else - { + else { // BH TOC 32 BH_TOC_Hash32 BH_TOC = {}; for (size_t i = 0; i < Big4Toc_vector.size(); i++) @@ -172,7 +169,7 @@ namespace big4 } - Big4Header write_init_header_section(std::vector& toc_compatible_filepaths, bool Big4IsBigFCheckState, std::ofstream& outfile) + bool write_init_header_section(std::vector& toc_compatible_filepaths, bool Big4IsBigFCheckState, std::ofstream& outfile) { Big4Header header = {}; if (Big4IsBigFCheckState == true) { @@ -186,32 +183,34 @@ namespace big4 header.number_files = BigToLittleUINT((uint32_t)toc_compatible_filepaths.size()); header.header_length = sizeof(Big4Header) + toc_compatible_filepaths.size() * sizeof(Big4Toc) + sizeof(Big4HeaderTailSettings); outfile.write(reinterpret_cast(&header), sizeof(Big4Header)); + if (!outfile.good()) { return false; } - return header; + return true; } - void update_header_section(const uint32_t& header_end_position, const uint32_t& archive_end_position, std::ofstream& outfile) + bool update_header_section(const uint32_t& header_end_position, const uint32_t& archive_end_position, std::ofstream& outfile) { uint32_t archive_length = archive_end_position; uint32_t header_length = BigToLittleUINT(header_end_position); outfile.seekp(4, std::ios::beg); outfile.write(reinterpret_cast(&archive_length), sizeof(4)); + if (!outfile.good()) { return false; } outfile.seekp(12, std::ios::beg); outfile.write(reinterpret_cast(&header_length), sizeof(4)); + if (!outfile.good()) { return false; } - return; + return true; } - auto write_toc_section(std::vector& toc_compatible_filepaths, std::ofstream& outfile, std::vector filepaths) + auto write_toc_section(std::vector& toc_compatible_filepaths, std::ofstream& outfile, const std::vector& filepaths) { std::vector toc_offset_offset_vector = {}; std::vector Big4Toc_vector = {}; - struct RESULTS { std::vector Big4Toc_vector; std::vector toc_offset_offset_vector; }; + struct RESULTS { std::vector Big4Toc_vector; std::vector toc_offset_offset_vector; bool bSuccess; }; for (size_t i = 0; i < filepaths.size(); i++) { - std::streampos currentPosition = outfile.tellp(); - uint32_t currentPositionUint32 = static_cast(currentPosition); - toc_offset_offset_vector.push_back(currentPositionUint32); + uint32_t current_toc_offsetvalue_position = static_cast(outfile.tellp()); + toc_offset_offset_vector.push_back(current_toc_offsetvalue_position); // This vector contains the offset to the toc offset and not the fileoffset itself. Big4Toc toc; toc.offset = 0; @@ -227,29 +226,36 @@ namespace big4 // Write TOC entry outfile.write(reinterpret_cast(&toc.offset), sizeof(toc.offset)); + if (!outfile.good()) { return RESULTS{ Big4Toc_vector, toc_offset_offset_vector, false }; } outfile.write(reinterpret_cast(&toc.size), sizeof(toc.size)); + if (!outfile.good()) { return RESULTS{ Big4Toc_vector, toc_offset_offset_vector, false }; } outfile.write(reinterpret_cast(filenameBuffer.data()), filenameBuffer.size()); + if (!outfile.good()) { return RESULTS{ Big4Toc_vector, toc_offset_offset_vector, false }; } outfile.write(reinterpret_cast(&toc.zero_terminator), sizeof(toc.zero_terminator)); + if (!outfile.good()) { return RESULTS{ Big4Toc_vector, toc_offset_offset_vector, false }; } } - return RESULTS{ Big4Toc_vector, toc_offset_offset_vector }; + + return RESULTS{ Big4Toc_vector, toc_offset_offset_vector, true }; } - void update_toc_section(std::vector& Big4Toc_vector, std::vector toc_offset_offset_vector, std::vector offset_vector, std::ofstream& outfile) + bool update_toc_section(std::vector& Big4Toc_vector, const std::vector& toc_offset_offset_vector, const std::vector& offset_vector, std::ofstream& outfile) { - + // Update the information in the TOC. for (size_t i = 0; i < toc_offset_offset_vector.size(); i++) { outfile.seekp(toc_offset_offset_vector[i], std::ios::beg); uint32_t offset = BigToLittleUINT(offset_vector[i]); Big4Toc_vector[i].offset = offset; outfile.write(reinterpret_cast(&offset), sizeof(4)); + if (!outfile.good()) { return false; } } - return; + return true; } - Big4HeaderTailSettings write_settings_section(std::ofstream& outfile, bool Big4CompressionCheckState) + bool write_settings_section(std::ofstream& outfile, bool Big4CompressionCheckState) { + // Check compression type. uint16_t bIsCompressed = 0; if (Big4CompressionCheckState == true) { bIsCompressed = 1; @@ -260,14 +266,98 @@ namespace big4 Big4HeaderTailSettings settings = {}; std::memcpy(settings.Version, "L282", 4); - settings.DJBHashKey = BigToLittleUINT((uint16_t)5381); // Replace with your desired value - settings.bIsCompressed = BigToLittleUINT(bIsCompressed); // Replace with your desired value + settings.DJBHashKey = BigToLittleUINT((uint16_t)5381); // Replace with your desired value (hash key) (Skate and most EA Games uses 5381) + settings.bIsCompressed = BigToLittleUINT(bIsCompressed); outfile.write(reinterpret_cast(&settings), sizeof(settings)); + if (!outfile.good()) { return false; } + + return true; + } + + bool write_filecontent_section(std::ofstream& outfile, const std::vector& filepaths, std::vector& offset_vector) + { + for (size_t i = 0; i < filepaths.size(); i++) { + uint32_t file_offset = static_cast(outfile.tellp()); + offset_vector.push_back(file_offset); + + // Check if file is empty and don't read it if it is. + if (isFileEmpty(filepaths[i])) { + continue; + } + + // Read file from filepaths list and push file buffer to archive. + std::ifstream infile(filepaths[i], std::ios::binary); + if (infile.is_open()) { + outfile << infile.rdbuf(); + infile.close(); + } + else { + std::cerr << "Error: Unable to open file: " << filepaths[i] << std::endl; + return false; + } - return settings; + // Add padding to the next index evenly divisible by 64 (if we need to) + if (i < filepaths.size() - 1) { + if (!addPadding(outfile)) { + return false; + } + } + } + return true; } - bool bundlebig4(std::vector filepaths_wstring, std::wstring selected_path, std::wstring save_bigfile_path, Big4Packer_Settings big4_packer_settings) + bool IsWithinBig4SizeLimit(const std::vector& filepaths) + { + static const uint8_t padding = 0; + uint8_t alignment = 64; + uint64_t size = 16; // Add header size right away. + uint64_t current_offset = 0; + + for (uint64_t i = 0; i < filepaths.size(); i++) { + size += 8; // Add size and offset size for each entry. + size += filepaths[i].length() + 1; // Add string length + zero terminator size. + } + + size += 8; // Add options / settings size. + + current_offset = size; + if (current_offset % alignment == 0) { + // No padding needed. + } + else { + uint64_t padding_size = alignment - (current_offset % alignment); + for (uint64_t i = 0; i < padding_size; i++) { + size += 1; // Add padding + } + } + + for (size_t i = 0; i < filepaths.size(); i++) + { + size += fs::file_size(filepaths[i]); // add file size + current_offset = size; + if (i < filepaths.size() - 1) { + if (current_offset % alignment == 0) { + // No padding needed. + } + else { + uint64_t padding_size = alignment - (current_offset % alignment); + for (size_t i = 0; i < padding_size; i++) { + size += 1; // Add padding + } + } + } + } + + // Check if final build size will be bigger than largest uint32 value. 0xFFFFFFFF | 4294967295 + if (size > 4294967295) { + return false; + } + else { + return true; + } + } + + bool bundlebig4(std::vector filepaths_wstring, std::wstring selected_path, std::wstring save_bigfile_path, Big4Packer_Settings big4_packer_settings) { std::vector Big4Toc_vector = {}; std::wstring top_level_path = removeLastFolder(selected_path); @@ -279,7 +369,6 @@ namespace big4 std::string OutArchivePath = WideStringToString(save_bigfile_path); std::string BigHeaderPath = replaceFileExtension(WideStringToString(save_bigfile_path), ".bh"); - for (size_t i = 0; i < filepaths_wstring.size(); i++) { std::wstring TOC_Compatible_Path = filepaths_wstring[i]; @@ -295,6 +384,13 @@ namespace big4 filepaths.push_back(WideStringToString(filepaths_wstring[i])); } + // Check if archive will be within the size limits. + if (!IsWithinBig4SizeLimit(filepaths)) + { + MessageBox(0, L"ERROR: Archive would exceed uint32 size limit of 4294967295 bytes!\nBuild process stopped.", L"Packer Prompt", MB_OK | MB_ICONERROR); + return false; + } + std::ofstream outfile(OutArchivePath, std::ios::binary); if (!outfile.is_open()) { @@ -303,61 +399,67 @@ namespace big4 } // Write Header. - Big4Header header = write_init_header_section(toc_compatible_filepaths, big4_packer_settings.Big4IsBigFCheckState, outfile); + if (!write_init_header_section(toc_compatible_filepaths, + big4_packer_settings.Big4IsBigFCheckState, outfile)) { + outfile.close(); + return false; + } // Write TOC entries. auto Big4Toc_RESULT = write_toc_section(toc_compatible_filepaths, outfile, filepaths); + if (!Big4Toc_RESULT.bSuccess) { + outfile.close(); + return false; + } Big4Toc_vector = Big4Toc_RESULT.Big4Toc_vector; // Write Settings. - Big4HeaderTailSettings settings = write_settings_section(outfile, big4_packer_settings.Big4CompressionCheckState); + if (!write_settings_section(outfile, + big4_packer_settings.Big4CompressionCheckState)) { + outfile.close(); + return false; + } // Save header size. uint32_t header_end_position = static_cast(outfile.tellp()); // Add padding to the next index evenly divisible by 64 (if we need to) - addPadding(outfile); + if (!addPadding(outfile)) { + outfile.close(); + return false; + } // Write files to archive. - for (size_t i = 0; i < filepaths.size(); i++) { - uint32_t file_offset = static_cast(outfile.tellp()); - offset_vector.push_back(file_offset); - - if (!isFileEmpty(filepaths[i])) - { - std::ifstream infile(filepaths[i], std::ios::binary); - if (infile.is_open()) { - outfile << infile.rdbuf(); - infile.close(); - } - else { - std::cerr << "Error: Unable to open file: " << filepaths[i] << std::endl; - return false; - } - - // Add padding to the next index evenly divisible by 64 (if we need to) - if (i < filepaths.size() - 1) - { - addPadding(outfile); - } - } + if (!write_filecontent_section(outfile, filepaths, offset_vector)) { + outfile.close(); + return false; } - // Save archive size. + // Save archive size and check if it's bigger than uint32 maximum. uint32_t archive_end_position = static_cast(outfile.tellp()); - // Update header and toc with saved information. - update_header_section(header_end_position, archive_end_position, outfile); - update_toc_section(Big4Toc_vector, Big4Toc_RESULT.toc_offset_offset_vector, offset_vector, outfile); + // Update header with saved information. + if (!update_header_section(header_end_position, archive_end_position, outfile)) { + outfile.close(); + return false; + } + // Update toc with saved information. + if (!update_toc_section(Big4Toc_vector, + Big4Toc_RESULT.toc_offset_offset_vector, offset_vector, outfile)) { + outfile.close(); + return false; + } - // Close filestream. + // Close filestream. (We have already done every we need with this filestream) outfile.close(); - // Write header file. - BuildBHVIV4(Big4Toc_vector, BigHeaderPath, archive_end_position, big4_packer_settings.IsHash64CheckState); - - std::cout << "Archive created successfully: " << OutArchivePath << std::endl; + // Write BH header file. + if (!BuildBHVIV4(Big4Toc_vector, BigHeaderPath, + archive_end_position, big4_packer_settings.IsHash64CheckState)) { + return false; + } + // Archive created successfully. return true; } diff --git a/RW4ArchiveTool/Archives/Parsers/big4_parser.h b/RW4ArchiveTool/Archives/Parsers/big4_parser.h index 7c63ae8..37f620d 100644 --- a/RW4ArchiveTool/Archives/Parsers/big4_parser.h +++ b/RW4ArchiveTool/Archives/Parsers/big4_parser.h @@ -9,255 +9,263 @@ namespace big4 { + + struct Big4Header + { + char magic[4]; + uint32_t length; + uint32_t number_files; + uint32_t header_length; + }; + + struct Big4Fat + { + uint32_t offset; + uint32_t size; + char filename[256]; + }; + + struct Big4HeaderTailSettings + { + char Version[4]; + uint16_t DJBHashKey; + uint16_t bIsCompressed; + }; + + struct ParsedBig4Struct + { + Big4Header header; + std::vector toc; + Big4HeaderTailSettings tail_settings; + }; + + bool unpack_empty_file(FILE* archive, std::wstring Filedirectory, std::wstring Filepath) + { + FILE* file = nullptr; + + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); + } - // There seems to be no way to set alignment on big4, so don't bother with it. - // You decide where the data starts and just point to it with the offset in Big4Fat. - // But the original packers aligns it with blocks of 64 bytes, so do that. :) - struct Big4Header - { - char magic[4]; - uint32_t length; - uint32_t number_files; - uint32_t header_length; - }; - - struct Big4Fat - { - uint32_t offset; - uint32_t size; - char filename[256]; - }; - - struct Big4HeaderTailSettings - { - char Version[4]; - uint16_t DJBHashKey; - uint16_t bIsCompressed; - }; - - struct ParsedBig4Struct - { - Big4Header header; - std::vector toc; - Big4HeaderTailSettings tail_settings; - }; - - - bool unpack_uncompressed_file(FILE* archive, DWORD size, std::wstring Filedirectory, std::wstring Filepath) - { - FILE* file = nullptr; + // Create empty file. + if (!IoTools::create_file(file, Filepath)) { + return false; + } - // Allocate memory for our file buffer - char* file_buffer = (char*)malloc(size); - if (file_buffer == NULL) { - perror("Error allocating memory"); - return false; - } + return true; + } - fread(file_buffer, size, 1, archive); // Read file into buffer + bool unpack_uncompressed_file(FILE* archive, DWORD size, std::wstring Filedirectory, std::wstring Filepath) + { + FILE* file = nullptr; - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } + // Allocate memory for our file buffer + char* file_buffer = (char*)malloc(size); + if (file_buffer == NULL) { + perror("Error allocating memory"); + return false; + } - // Write to file. - if (!IoTools::write_file(file, Filepath, (char*)file_buffer, size)) - { - free(file_buffer); - return false; - } + // Read file into buffer + fread(file_buffer, size, 1, archive); - free(file_buffer); - return true; + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); } - bool unpack_refpack_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) - { - FILE* file = nullptr; + // Write to file. + if (!IoTools::write_file(file, Filepath, (char*)file_buffer, size)) { + free(file_buffer); + return false; + } - // Allocate memory for our file buffer - char* file_buffer = (char*)malloc(SIZE); - if (file_buffer == NULL) { - perror("Error allocating memory"); - return false; - } + free(file_buffer); + return true; + } - fread(file_buffer, SIZE, 1, archive); // Read file into buffer + bool unpack_refpack_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) + { + FILE* file = nullptr; - std::vector decompression_in_buffer_vector(file_buffer, file_buffer + SIZE); - std::vector decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); - size_t decompressed_size = decompression_out_buffer_vector.size(); + // Allocate memory for our file buffer + char* file_buffer = (char*)malloc(SIZE); + if (file_buffer == NULL) { + perror("Error allocating memory"); + return false; + } - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } + // Read file into buffer + fread(file_buffer, SIZE, 1, archive); - // Write to file. - if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), decompressed_size)) - { - free(file_buffer); - return false; - } + // Decompress file + std::vector decompression_in_buffer_vector(file_buffer, file_buffer + SIZE); + std::vector decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); + size_t decompressed_size = decompression_out_buffer_vector.size(); - free(file_buffer); - return true; + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); } - // Function to unpack files from a big4 archive. - auto parse_big4_archive(const wchar_t* archiveName, bool unpack, int64_t selected_file_index) { - - struct RESULT { std::vector parsed_info; bool success; }; + // Write to file. + if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), decompressed_size)) { + free(file_buffer); + return false; + } - // Declare local variables. - FILE* archive = nullptr; - std::vector Archive_Parse_Struct_vector = {}; + free(file_buffer); + return true; + } + + // Function to unpack files from a big4 archive. + auto parse_big4_archive(const wchar_t* archiveName, bool unpack, int64_t selected_file_index) + { + struct RESULT { std::vector parsed_info; bool success; }; // Return type + + // Declare local variables. + FILE* archive = nullptr; + std::vector Archive_Parse_Struct_vector = {}; + ParsedBig4Struct parsed_big4_struct = {}; + Big4Header big4_header = {}; + + // Open archive. + _wfopen_s(&archive, archiveName, L"rb"); + if (archive == NULL) { + perror("Error opening archive"); + return RESULT{ Archive_Parse_Struct_vector , false }; + } - ParsedBig4Struct parsed_big4_struct = {}; - Big4Header big4_header = {}; + // Save start position. + uint64_t start_of_archive = _ftelli64(archive); - _wfopen_s(&archive, archiveName, L"rb"); - if (archive == NULL) { - perror("Error opening archive"); - return RESULT{ Archive_Parse_Struct_vector , false }; - } + // Save archive size and go back to start position. + fseek(archive, 0, SEEK_END); + uint64_t archive_size = _ftelli64(archive); + fseek(archive, start_of_archive, SEEK_SET); - // Save start position. - uint64_t start_of_archive = _ftelli64(archive); + // Read header into struct. + fread(&big4_header, sizeof(big4_header), 1, archive); + big4_header.header_length = BigToLittleUINT(big4_header.header_length); + //big4_header.length = BigToLittleUINT(big4_header.length); // This value is already little endian for some reason. + big4_header.number_files = BigToLittleUINT(big4_header.number_files); - // Save archive size and go back to start position. - fseek(archive, 0, SEEK_END); - uint64_t archive_size = _ftelli64(archive); - fseek(archive, start_of_archive, SEEK_SET); + if (big4_header.header_length > archive_size || + big4_header.length > archive_size || big4_header.number_files > archive_size) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } - // Read header into struct. - fread(&big4_header, sizeof(big4_header), 1, archive); - big4_header.header_length = BigToLittleUINT(big4_header.header_length); - //big4_header.length = BigToLittleUINT(big4_header.length); // This value is already little endian for some reason. - big4_header.number_files = BigToLittleUINT(big4_header.number_files); + parsed_big4_struct.header = big4_header; - if (big4_header.header_length > archive_size || big4_header.length > archive_size || big4_header.number_files > archive_size) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; + for (size_t i = 0; i < big4_header.number_files; i++) + { + std::wstring wide_archiv_path = archiveName; + std::wstring directory = ParseFilePath(wide_archiv_path).first; + std::wstring full_out_filepath = directory; + std::wstring full_out_file_directory = directory; + Archive_Parse_Struct Parsed_Archive_Struct = {}; + + // Save location. + uint32_t next_toc_offset = 0; + next_toc_offset = _ftelli64(archive); + Parsed_Archive_Struct.toc_offset = next_toc_offset; // Set current toc offset. + + // Get file information and convert to little endian. + Big4Fat big4_fat = {}; + fread(&big4_fat, sizeof(big4_fat), 1, archive); + big4_fat.offset = BigToLittleUINT(big4_fat.offset); + big4_fat.size = BigToLittleUINT(big4_fat.size); + parsed_big4_struct.toc.push_back(big4_fat); + + // Check value bounds. + if (big4_fat.size > archive_size || big4_fat.offset > archive_size) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } + + // Get filename length. + size_t filename_length = 0; + filename_length = std::strlen(big4_fat.filename); + + // Calculate the next toc index offset. + next_toc_offset = next_toc_offset + sizeof(big4_fat.offset) + sizeof(big4_fat.size) + filename_length + 1; + + // Convert the string to a filesystem path. + std::filesystem::path path(big4_fat.filename); + + // Build full path and directory. + full_out_file_directory += path.parent_path().wstring(); + full_out_filepath += (path.parent_path().wstring() + L"/" + path.filename().wstring()); + std::replace(full_out_file_directory.begin(), full_out_file_directory.end(), L'/', L'\\'); + std::replace(full_out_filepath.begin(), full_out_filepath.end(), L'/', L'\\'); + + // Seek to file offset, read first 4 bytes and go back to file offset. + _fseeki64(archive, big4_fat.offset, SEEK_SET); + uint16_t compression_type = 0; + fread(&compression_type, sizeof(compression_type), 1, archive); + compression_type = BigToLittleUINT(compression_type); // Convert ztype to little endian. + _fseeki64(archive, big4_fat.offset, SEEK_SET); // Seek back to offset + + // Set Parsed_Archive struct members. + Parsed_Archive_Struct.filename = path.string(); + Parsed_Archive_Struct.file_size = big4_fat.size; + Parsed_Archive_Struct.file_offset = big4_fat.offset; + + // Parse and unpack conditions. + bool full_archive_unpack = unpack && selected_file_index == -1; // Do a full archive unpack. + bool single_archive_unpack = unpack && selected_file_index == i; // Do a single file archive unpack. + + // Set compression type string for UI. + if (compression_type == 0x10FB) { + strcpy_s(Parsed_Archive_Struct.ztype, "REFPACK"); + } + else { + strcpy_s(Parsed_Archive_Struct.ztype, "NONE"); + } + + // Run the unpacking functions. + if ((unpack == true && compression_type == 0x10FB && big4_fat.size > 0) && (full_archive_unpack || single_archive_unpack)) { // Compressed file unpacking. + if (!unpack_refpack_file(archive, big4_fat.size, full_out_file_directory, full_out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; } - - parsed_big4_struct.header = big4_header; - - for (size_t i = 0; i < big4_header.number_files; i++) - { - std::wstring wide_archiv_path = archiveName; - std::wstring directory = ParseFilePath(wide_archiv_path).first; - std::wstring full_out_filepath = directory; - std::wstring full_out_file_directory = directory; - Archive_Parse_Struct Parsed_Archive_Struct = {}; - - // Save location. - uint32_t next_toc_offset = 0; - next_toc_offset = _ftelli64(archive); - Parsed_Archive_Struct.toc_offset = next_toc_offset; // Set current toc offset. - - // Get file information and convert to little endian. - Big4Fat big4_fat = {}; - fread(&big4_fat, sizeof(big4_fat), 1, archive); - big4_fat.offset = BigToLittleUINT(big4_fat.offset); - big4_fat.size = BigToLittleUINT(big4_fat.size); - - if (big4_fat.size > archive_size || big4_fat.offset > archive_size) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - - parsed_big4_struct.toc.push_back(big4_fat); - - // Get filename length. - size_t filename_length = 0; - filename_length = std::strlen(big4_fat.filename); - - // Calculate the next toc index offset. - next_toc_offset = next_toc_offset + sizeof(big4_fat.offset) + sizeof(big4_fat.size) + filename_length + 1; - - // Convert the string to a filesystem path. - std::filesystem::path path(big4_fat.filename); - - // Build full path and directory. - full_out_file_directory += path.parent_path().wstring(); - full_out_filepath += (path.parent_path().wstring() + L"/" + path.filename().wstring()); - std::replace(full_out_file_directory.begin(), full_out_file_directory.end(), L'/', L'\\'); - std::replace(full_out_filepath.begin(), full_out_filepath.end(), L'/', L'\\'); - - // Seek to file offset, read first 4 bytes and go back to file offset. - _fseeki64(archive, big4_fat.offset, SEEK_SET); - uint16_t compression_type = 0; - fread(&compression_type, sizeof(compression_type), 1, archive); - compression_type = BigToLittleUINT(compression_type); // Convert ztype to little endian. - _fseeki64(archive, big4_fat.offset, SEEK_SET); // Seek back to offset - - // Set Parsed_Archive struct members. - Parsed_Archive_Struct.filename = path.string(); - Parsed_Archive_Struct.file_size = big4_fat.size; - Parsed_Archive_Struct.file_offset = big4_fat.offset; - - bool full_archive_unpack = unpack && selected_file_index == -1; // Do a full archive unpack. - bool single_archive_unpack = unpack && selected_file_index == i; // Do a single file archive unpack. - - switch (compression_type) - { - case 0x10FB: - strcpy_s(Parsed_Archive_Struct.ztype, "REFPACK"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (unpack_refpack_file(archive, big4_fat.size, full_out_file_directory, full_out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - break; - default: - strcpy_s(Parsed_Archive_Struct.ztype, "NONE"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (unpack_uncompressed_file(archive, big4_fat.size, full_out_file_directory, full_out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - - break; - } - - dont_unpack_loc: - Archive_Parse_Struct_vector.push_back(Parsed_Archive_Struct); - - _fseeki64(archive, next_toc_offset, SEEK_SET); + } + else if ((unpack == true && compression_type != 0x10FB && big4_fat.size > 0) && (full_archive_unpack || single_archive_unpack)) { // Uncompressed file unpacking. + if (!unpack_uncompressed_file(archive, big4_fat.size, full_out_file_directory, full_out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; } + } + else if ((unpack == true && big4_fat.size == 0) && (full_archive_unpack || single_archive_unpack)) { // Empty file unpacking. + if (!unpack_empty_file(archive, full_out_file_directory, full_out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } + } - // Get parse the last settings details. - Big4HeaderTailSettings big4header_tail_settings = {}; - fread(&big4header_tail_settings, sizeof(big4header_tail_settings), 1, archive); - big4header_tail_settings.bIsCompressed = BigToLittleUINT(big4header_tail_settings.bIsCompressed); - big4header_tail_settings.DJBHashKey = BigToLittleUINT(big4header_tail_settings.DJBHashKey); - parsed_big4_struct.tail_settings = big4header_tail_settings; - - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector, true }; + Archive_Parse_Struct_vector.push_back(Parsed_Archive_Struct); + _fseeki64(archive, next_toc_offset, SEEK_SET); } + + // Get parse the last settings details. + Big4HeaderTailSettings big4header_tail_settings = {}; + fread(&big4header_tail_settings, sizeof(big4header_tail_settings), 1, archive); + big4header_tail_settings.bIsCompressed = BigToLittleUINT(big4header_tail_settings.bIsCompressed); + big4header_tail_settings.DJBHashKey = BigToLittleUINT(big4header_tail_settings.DJBHashKey); + parsed_big4_struct.tail_settings = big4header_tail_settings; + + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector, true }; + } } \ No newline at end of file diff --git a/RW4ArchiveTool/Archives/Parsers/big_eb_parser.h b/RW4ArchiveTool/Archives/Parsers/big_eb_parser.h index 04c2805..b4ae5d9 100644 --- a/RW4ArchiveTool/Archives/Parsers/big_eb_parser.h +++ b/RW4ArchiveTool/Archives/Parsers/big_eb_parser.h @@ -3,6 +3,7 @@ #pragma once #include "refpack/refpackd.h" #include "Zlib/Include/zlib-1.3/zlib.h" +#include "lzx/lzx.h" #include "IoTools.h" #include "../Structs/SF_Structs.h" #include "../../Tools/Paths/Paths.h" @@ -10,575 +11,553 @@ namespace big_eb { - struct BigEBArchiveHeader { - WORD Signature; - WORD HeaderVersion; - DWORD FileAmount; - WORD Flags; - BYTE Alignment; - BYTE Reserved; - DWORD NamesOffset; - DWORD NamesSize; - BYTE FilenameLength; - BYTE FoldernameLength; - WORD FolderAmount; - UINT64 ArchiveSize; - DWORD FatSize; - }; - - const WORD FLAG_64BITHASH = 0x0001; - - const DWORD FAT_DIRECTORY = 0; - const DWORD FAT_FILE = 1; - - struct SortOrder { - const WORD HASH = 0x0010; - const WORD ALPHABETICAL = 0x0020; - const WORD FAT = 0x0040; - const WORD OFFSET = 0x0080; - }; - - enum CompressionType { - NONE = 0, - REFPACK = 1, - REFPACK_CHUNKED = 2, - ZLIB_CHUNKED = 3, - LZX = 4 - }; - - struct ChunkPackHeader { - char ID[8]; - uint32_t VersionNum; - uint32_t uncompressedLength; - uint32_t blockSize; - uint32_t numSegments; - uint32_t alignedTo; - }; - - struct ChunkBlockHeader { - uint32_t chunkSizeCompressed; - uint32_t compressionType; - }; - - struct TOCIndex { - DWORD offset; - DWORD compressed_size; - DWORD size; - DWORD hash1; - DWORD hash2; // Used only in 64-bit hash mode. - }; - - bool unpack_empty_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) - { - FILE* file = nullptr; - - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } - - // Create empty file. - if (!IoTools::create_file(file, Filepath)) - { - return false; - } - - return true; + struct BigEBArchiveHeader { + WORD Signature; + WORD HeaderVersion; + DWORD FileAmount; + WORD Flags; + BYTE Alignment; + BYTE Reserved; + DWORD NamesOffset; + DWORD NamesSize; + BYTE FilenameLength; + BYTE FoldernameLength; + WORD FolderAmount; + UINT64 ArchiveSize; + DWORD FatSize; + }; + + const WORD FLAG_64BITHASH = 0x0001; + + const DWORD FAT_DIRECTORY = 0; + const DWORD FAT_FILE = 1; + + struct SortOrder { + const WORD HASH = 0x0010; + const WORD ALPHABETICAL = 0x0020; + const WORD FAT = 0x0040; + const WORD OFFSET = 0x0080; + }; + + enum CompressionType { + NONE = 0, + REFPACK = 1, + REFPACK_CHUNKED = 2, + ZLIB_CHUNKED = 3, + LZX = 4 + }; + + struct ChunkPackHeader { + char ID[8]; + uint32_t VersionNum; + uint32_t uncompressedLength; + uint32_t blockSize; + uint32_t numSegments; + uint32_t alignedTo; + }; + + struct ChunkBlockHeader { + uint32_t chunkSizeCompressed; + uint32_t compressionType; + }; + + struct TOCIndex { + DWORD offset; + DWORD compressed_size; + DWORD size; + DWORD hash1; + DWORD hash2; // Used only in 64-bit hash mode. + }; + + bool unpack_empty_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) + { + FILE* file = nullptr; + + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); } - bool decompress_deflate(const unsigned char* compressedData, size_t compressedSize, unsigned char* decompressedData, size_t& decompressedSize) { - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - stream.avail_in = static_cast(compressedSize); - stream.next_in = const_cast(compressedData); - - if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) { - std::cerr << "Error initializing zlib inflate for deflate format." << std::endl; - return false; - } + // Create empty file. + if (!IoTools::create_file(file, Filepath)) { + return false; + } - stream.avail_out = static_cast(decompressedSize); - stream.next_out = decompressedData; + return true; + } + + bool decompress_deflate(const unsigned char* compressedData, size_t compressedSize, unsigned char* decompressedData, size_t& decompressedSize) + { + z_stream stream; + stream.zalloc = Z_NULL; + stream.zfree = Z_NULL; + stream.opaque = Z_NULL; + stream.avail_in = static_cast(compressedSize); + stream.next_in = const_cast(compressedData); + + if (inflateInit2(&stream, -MAX_WBITS) != Z_OK) { + std::cerr << "Error initializing zlib inflate for deflate format." << std::endl; + return false; + } - if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { - std::cerr << "Error in zlib decompression for deflate format." << std::endl; - inflateEnd(&stream); - return false; - } + stream.avail_out = static_cast(decompressedSize); + stream.next_out = decompressedData; - decompressedSize = stream.total_out; + if (inflate(&stream, Z_FINISH) != Z_STREAM_END) { + std::cerr << "Error in zlib decompression for deflate format." << std::endl; + inflateEnd(&stream); + return false; + } - if (inflateEnd(&stream) != Z_OK) { - std::cerr << "Error ending zlib inflate for deflate format." << std::endl; - return false; - } + decompressedSize = stream.total_out; - return true; + if (inflateEnd(&stream) != Z_OK) { + std::cerr << "Error ending zlib inflate for deflate format." << std::endl; + return false; } - bool chunk_decompress(FILE* archive, std::wstring Filedirectory, std::wstring Filepath) - { - // Local Variables. - std::vector decompression_out_buffer_vector = {}; - size_t decompressed_size = 0; - - ChunkPackHeader chunk_pack_header = {}; - fread(&chunk_pack_header, sizeof(chunk_pack_header), 1, archive); - chunk_pack_header.alignedTo = BigToLittleUINT(chunk_pack_header.alignedTo); - chunk_pack_header.blockSize = BigToLittleUINT(chunk_pack_header.blockSize); - chunk_pack_header.numSegments = BigToLittleUINT(chunk_pack_header.numSegments); - chunk_pack_header.uncompressedLength = BigToLittleUINT(chunk_pack_header.uncompressedLength); - chunk_pack_header.VersionNum = BigToLittleUINT(chunk_pack_header.VersionNum); - - for (size_t i = 0; i < chunk_pack_header.numSegments; i++) - { - // Get the current location. - size_t current_location = _ftelli64(archive); - std::cout << current_location << "\n"; - - // Extract the last hexadecimal digit - size_t lastDigit = current_location % 0x10; - - // Check if the last digit is greater than 0x08 or 0. - if (lastDigit > 0x08 || lastDigit == 0) { - std::cout << "The offset ends with a value greater than or equal to 0x08." << std::endl; - current_location = roundUpToMultiple(current_location, chunk_pack_header.alignedTo); - current_location += 8; - } - else { - std::cout << "The offset does not end with a value greater than or equal to 0x08." << std::endl; - current_location = roundUpToMultiple(current_location, 8); - } + return true; + } - _fseeki64(archive, current_location, SEEK_SET); // Seek to closest aligned location recognized by the BIG EB v3 format. + bool chunk_decompress(FILE* archive, std::wstring Filedirectory, std::wstring Filepath) + { + // Local Variables. + std::vector decompression_out_buffer_vector = {}; + size_t decompressed_size = 0; - // Read RW chunk block header. - ChunkBlockHeader block_header = {}; - fread(&block_header, sizeof(block_header), 1, archive); - block_header.chunkSizeCompressed = BigToLittleUINT(block_header.chunkSizeCompressed); - block_header.compressionType = BigToLittleUINT(block_header.compressionType); + ChunkPackHeader chunk_pack_header = {}; + fread(&chunk_pack_header, sizeof(chunk_pack_header), 1, archive); + chunk_pack_header.alignedTo = BigToLittleUINT(chunk_pack_header.alignedTo); + chunk_pack_header.blockSize = BigToLittleUINT(chunk_pack_header.blockSize); + chunk_pack_header.numSegments = BigToLittleUINT(chunk_pack_header.numSegments); + chunk_pack_header.uncompressedLength = BigToLittleUINT(chunk_pack_header.uncompressedLength); + chunk_pack_header.VersionNum = BigToLittleUINT(chunk_pack_header.VersionNum); - // Allocate memory for our file buffer - char* file_buffer = (char*)malloc(block_header.chunkSizeCompressed); - if (file_buffer == NULL) { - perror("Error allocating memory"); - return false; - } + for (size_t i = 0; i < chunk_pack_header.numSegments; i++) + { + // Get the current location. + size_t current_location = _ftelli64(archive); + std::cout << current_location << "\n"; + + // Extract the last hexadecimal digit + size_t lastDigit = current_location % 0x10; + + // Check if the last digit is greater than 0x08 or 0. + if (lastDigit > 0x08 || lastDigit == 0) { + std::cout << "The offset ends with a value greater than or equal to 0x08." << std::endl; + current_location = roundUpToMultiple(current_location, chunk_pack_header.alignedTo); + current_location += 8; + } + else { + std::cout << "The offset does not end with a value greater than or equal to 0x08." << std::endl; + current_location = roundUpToMultiple(current_location, 8); + } + + _fseeki64(archive, current_location, SEEK_SET); // Seek to closest aligned location recognized by the BIG EB v3 format. + + // Read RW chunk block header. + ChunkBlockHeader block_header = {}; + fread(&block_header, sizeof(block_header), 1, archive); + block_header.chunkSizeCompressed = BigToLittleUINT(block_header.chunkSizeCompressed); + block_header.compressionType = BigToLittleUINT(block_header.compressionType); + + // Allocate memory for our file buffer + char* file_buffer = (char*)malloc(block_header.chunkSizeCompressed); + if (file_buffer == NULL) { + perror("Error allocating memory"); + return false; + } + + // Read chunk into file buffer. + fread(file_buffer, block_header.chunkSizeCompressed, 1, archive); // Read file into buffer + std::vector decompression_in_buffer_vector(file_buffer, file_buffer + block_header.chunkSizeCompressed); + + // Check if the chunk is compressed. + if (block_header.compressionType == 1) // Chunk is compressed with zlib. + { + // Allocate a buffer for the decompressed data + size_t chunk_decompressed_size = chunk_pack_header.uncompressedLength; + unsigned char* decompressedData = new unsigned char[chunk_decompressed_size]; + + // Decompress the data + if (decompress_deflate(decompression_in_buffer_vector.data(), block_header.chunkSizeCompressed, decompressedData, chunk_decompressed_size)) { + decompressed_size += chunk_decompressed_size; + decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), decompressedData, decompressedData + chunk_decompressed_size); + delete[] decompressedData; + } + else { + std::cerr << "Decompression failed." << std::endl; + MessageBoxA(0, "Decompression failed! Reach out to GHFear for support.", "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + free(file_buffer); + return false; + } + } + else if (block_header.compressionType == 2) // Chunk is compressed with refpack. + { + std::vector chunk_decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); + decompressed_size += chunk_decompression_out_buffer_vector.size(); + decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), chunk_decompression_out_buffer_vector.begin(), chunk_decompression_out_buffer_vector.end()); + } + else if (block_header.compressionType == 3) // Chunk is compressed with lzx. (I am working on adding support for this.) + { + size_t chunk_decompressed_size = chunk_pack_header.uncompressedLength; + size_t compressed_size = block_header.chunkSizeCompressed; + MessageBoxA(0, "Unsupported compression type (LZX)! I am already working on it. :)", "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + free(file_buffer); + return false; + } + else if (block_header.compressionType == 4) // Chunk is not compressed + { + decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), decompression_in_buffer_vector.begin(), decompression_in_buffer_vector.end()); + } + else + { + MessageBoxA(0, "Unknown compression type! Reach out to GHFear for support.", "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + free(file_buffer); + return false; + } - // Read chunk into file buffer. - fread(file_buffer, block_header.chunkSizeCompressed, 1, archive); // Read file into buffer - std::vector decompression_in_buffer_vector(file_buffer, file_buffer + block_header.chunkSizeCompressed); + free(file_buffer); + } - // Check if the chunk is compressed. - if (block_header.compressionType == 2) // Chunk is compressed with refpack. - { - std::vector chunk_decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); - decompressed_size += chunk_decompression_out_buffer_vector.size(); - decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), chunk_decompression_out_buffer_vector.begin(), chunk_decompression_out_buffer_vector.end()); - } - else if (block_header.compressionType == 4) // Chunk is not compressed - { - decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), decompression_in_buffer_vector.begin(), decompression_in_buffer_vector.end()); - } - else if (block_header.compressionType == 1) // Chunk is compressed with zlib. - { - // Allocate a buffer for the decompressed data - size_t chunk_decompressed_size = chunk_pack_header.uncompressedLength; - unsigned char* decompressedData = new unsigned char[chunk_decompressed_size]; - - // Decompress the data - if (decompress_deflate(decompression_in_buffer_vector.data(), block_header.chunkSizeCompressed, decompressedData, chunk_decompressed_size)) { - decompressed_size += chunk_decompressed_size; - decompression_out_buffer_vector.insert(decompression_out_buffer_vector.end(), decompressedData, decompressedData + chunk_decompressed_size); - delete[] decompressedData; - } - else { - std::cerr << "Decompression failed." << std::endl; - MessageBoxA(0, "Decompression failed! Reach out to GHFear for support.", "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - free(file_buffer); - return false; - } - } - else - { - MessageBoxA(0, "Unknown compression type! Reach out to GHFear for support.", "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - free(file_buffer); - return false; - } + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); + } - free(file_buffer); - } + // Write to file. + FILE* file = nullptr; + if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), chunk_pack_header.uncompressedLength)) { + return false; + } - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) - { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else - { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } + return true; + } - // Write to file. - FILE* file = nullptr; - if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), chunk_pack_header.uncompressedLength)) - { - return false; - } + bool unpack_uncompressed_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) + { + // Allocate memory for our file buffer + char* file_buffer = (char*)malloc(SIZE); + if (file_buffer == NULL) { + perror("Error allocating memory"); + return false; + } - return true; + fread(file_buffer, SIZE, 1, archive); // Read file into buffer + + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); + } + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); } - bool unpack_uncompressed_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) - { - // Allocate memory for our file buffer - char* file_buffer = (char*)malloc(SIZE); - if (file_buffer == NULL) { - perror("Error allocating memory"); - return false; - } + // Write to file. + FILE* file = nullptr; + if (!IoTools::write_file(file, Filepath, file_buffer, SIZE)) { + free(file_buffer); + return false; + } - fread(file_buffer, SIZE, 1, archive); // Read file into buffer + free(file_buffer); + return true; + } + + void failed_to_unpack_messagebox(std::string input_msg) + { + std::string output_string = " Failed out unpack."; + output_string += input_msg; + MessageBoxA(0, output_string.c_str(), "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + } + + bool unpack_refpack_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) + { + // Allocate memory for our file buffer + char* file_buffer = (char*)malloc(SIZE); + if (file_buffer == NULL) { + perror("Error allocating memory"); + return false; + } - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } + // Read file into buffer + fread(file_buffer, SIZE, 1, archive); - // Write to file. - FILE* file = nullptr; - if (!IoTools::write_file(file, Filepath, file_buffer, SIZE)) - { - free(file_buffer); - return false; - } + // Decompress + std::vector decompression_in_buffer_vector(file_buffer, file_buffer + SIZE); + std::vector decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); + size_t decompressed_size = decompression_out_buffer_vector.size(); - free(file_buffer); - return true; + // Attempt to create the directory + if (CreateDirectoryRecursively(Filedirectory.c_str())) { + wprintf(L"Directory created: %s\n", Filedirectory.c_str()); } - - void failed_to_unpack_messagebox(std::string input_msg) - { - std::string output_string = " Failed out unpack."; - output_string += input_msg; - MessageBoxA(0, output_string.c_str(), "Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + else { + wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); } - bool unpack_refpack_file(FILE* archive, DWORD SIZE, std::wstring Filedirectory, std::wstring Filepath) - { - // Allocate memory for our file buffer - char* file_buffer = (char*)malloc(SIZE); - if (file_buffer == NULL) { - perror("Error allocating memory"); - return false; - } + // Write to file. + FILE* file = nullptr; + if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), decompressed_size)) { + free(file_buffer); + return false; + } - fread(file_buffer, SIZE, 1, archive); // Read file into buffer + free(file_buffer); + return true; + } + + // Function to parse files from a big eb archive. (Doesn't decompress yet) + auto parse_big_eb_archive(const wchar_t* archiveName, bool unpack, int64_t selected_file_index) + { + struct RESULT { std::vector parsed_info; bool success; }; + + // Declare local variables. + FILE* archive = nullptr; + BigEBArchiveHeader archive_header = {}; + std::vector Archive_Parse_Struct_vector = {}; + std::vector toc_offset_Vector; + std::vector offset_Vector; + std::vector size_Vector; + std::vector compression_type_Vector; + + DWORD folders_offset = 0; + + _wfopen_s(&archive, archiveName, L"rb"); + if (archive == NULL) { + perror("Error opening archive"); + return RESULT{ Archive_Parse_Struct_vector , false }; + } - std::vector decompression_in_buffer_vector(file_buffer, file_buffer + SIZE); - std::vector decompression_out_buffer_vector = refpack::decompress(decompression_in_buffer_vector); - size_t decompressed_size = decompression_out_buffer_vector.size(); + // Save start position. + uint64_t start_of_archive = _ftelli64(archive); + + // Save archive size and go back to start position. + fseek(archive, 0, SEEK_END); + uint64_t archive_size = _ftelli64(archive); + fseek(archive, start_of_archive, SEEK_SET); + + // Read archive header and convert to little endian. + fread(&archive_header, sizeof(archive_header), 1, archive); + archive_header.Signature = BigToLittleUINT(archive_header.Signature); + archive_header.HeaderVersion = BigToLittleUINT(archive_header.HeaderVersion); + archive_header.FileAmount = BigToLittleUINT(archive_header.FileAmount); + archive_header.Flags = BigToLittleUINT(archive_header.Flags); + archive_header.NamesOffset = BigToLittleUINT(archive_header.NamesOffset); + archive_header.NamesSize = BigToLittleUINT(archive_header.NamesSize); + archive_header.FolderAmount = BigToLittleUINT(archive_header.FolderAmount); + archive_header.ArchiveSize = BigToLittleUINT(archive_header.ArchiveSize); + archive_header.FatSize = BigToLittleUINT(archive_header.FatSize); + + if (archive_header.FileAmount > archive_size || + archive_header.ArchiveSize > archive_size || archive_header.FatSize > archive_size) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } - // Attempt to create the directory - if (CreateDirectoryRecursively(Filedirectory.c_str())) { - wprintf(L"Directory created: %s\n", Filedirectory.c_str()); - } - else { - wprintf(L"Failed to create directory or directory already exists: %s\n", Filedirectory.c_str()); - } + // Set folder offset. + folders_offset = archive_header.FileAmount; + folders_offset *= archive_header.FilenameLength; + folders_offset += archive_header.NamesOffset; + folders_offset = roundUpToMultiple(folders_offset, 0x10); + archive_header.FilenameLength -= 2; - // Write to file. - FILE* file = nullptr; - if (!IoTools::write_file(file, Filepath, (char*)decompression_out_buffer_vector.data(), decompressed_size)) - { - free(file_buffer); - return false; - } + _fseeki64(archive, 0x30, SEEK_SET); + for (size_t i = 0; i < archive_header.FileAmount; i++) + { + // Build toc_index (or whatever we would call this) + TOCIndex toc_index = {}; + toc_offset_Vector.push_back(_ftelli64(archive)); + fread(&toc_index, sizeof(toc_index), 1, archive); + toc_index.offset = BigToLittleUINT(toc_index.offset); + toc_index.compressed_size = BigToLittleUINT(toc_index.compressed_size); + toc_index.size = BigToLittleUINT(toc_index.size); + toc_index.hash1 = BigToLittleUINT(toc_index.hash1); + toc_index.hash2 = BigToLittleUINT(toc_index.hash2); + + if (toc_index.offset > archive_size || + toc_index.compressed_size > archive_size || toc_index.size > archive_size) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } + + if (!(archive_header.Flags & FLAG_64BITHASH)) { + _fseeki64(archive, -4, SEEK_CUR); + } + + // Push values into vector. + UINT64 OFFSET64 = toc_index.offset; + OFFSET64 = OFFSET64 << archive_header.Alignment; + offset_Vector.push_back(OFFSET64); + size_Vector.push_back(toc_index.size); + } - free(file_buffer); - return true; + for (size_t i = 0; i < archive_header.FileAmount; i++) + { + // Read compression type. (This doesn't exist on all versions of EB V3 and you may need to figure this out some other way.) + BYTE compression_type = 0; + fread(&compression_type, sizeof(compression_type), 1, archive); + compression_type_Vector.push_back(compression_type); } - // Function to parse files from a big eb archive. (Doesn't decompress yet) - auto parse_big_eb_archive(const wchar_t* archiveName, bool unpack, int64_t selected_file_index) + _fseeki64(archive, archive_header.NamesOffset, SEEK_SET); + for (size_t i = 0; i < archive_header.FileAmount; i++) { - struct RESULT { std::vector parsed_info; bool success; }; - - // Declare local variables. - FILE* archive = nullptr; - BigEBArchiveHeader archive_header = {}; - std::vector Archive_Parse_Struct_vector = {}; - std::vector toc_offset_Vector; - std::vector offset_Vector; - std::vector size_Vector; - std::vector compression_type_Vector; - - DWORD folders_offset = 0; - - _wfopen_s(&archive, archiveName, L"rb"); - if (archive == NULL) { - perror("Error opening archive"); + // Declare local variables. + Archive_Parse_Struct Parsed_Archive_Struct = {}; + + std::wstring wide_archiv_path = archiveName; + std::wstring directory = ParseFilePath(wide_archiv_path).first; + std::wstring out_filepath = directory; + std::wstring out_filedirectory = directory; + + // Read the folder number and convert to little endian. + WORD folder_number = 0; + fread(&folder_number, sizeof(folder_number), 1, archive); + folder_number = BigToLittleUINT(folder_number); + + // Read and build directory. (Stage 1: Filename) + std::vector name_buffer(archive_header.FilenameLength); + fread(name_buffer.data(), archive_header.FilenameLength, 1, archive); + std::string name(name_buffer.begin(), std::find(name_buffer.begin(), name_buffer.end(), '\0')); + std::string filename = name; + + DWORD next_name_offset = _ftelli64(archive); // Save the current location as next name offset location. + DWORD foldername_offset = folder_number; + foldername_offset *= archive_header.FoldernameLength; + foldername_offset += folders_offset; + + _fseeki64(archive, foldername_offset, SEEK_SET); // Seek to foldername location in archive. + + // Read and build directory. (Stage 2: directory) + std::vector folder_buffer(archive_header.FoldernameLength); + fread(folder_buffer.data(), archive_header.FoldernameLength, 1, archive); + std::string folder(folder_buffer.begin(), std::find(folder_buffer.begin(), folder_buffer.end(), '\0')); + std::string final_extracted_filepath = folder; + final_extracted_filepath += "/"; + final_extracted_filepath += name; + + _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. + + // Prepare final wide character strings. (Stage 3: Build proper path) + out_filedirectory += to_wstring(folder); + std::replace(out_filepath.begin(), out_filepath.end(), L'/', L'\\'); + std::replace(out_filedirectory.begin(), out_filedirectory.end(), L'/', L'\\'); + out_filepath += to_wstring(final_extracted_filepath); + + if (!unpack) { + // Set Parsed_Archive struct members. + Parsed_Archive_Struct.filename = folder + name; + Parsed_Archive_Struct.file_size = size_Vector[i]; + Parsed_Archive_Struct.file_offset = offset_Vector[i]; + Parsed_Archive_Struct.toc_offset = toc_offset_Vector[i]; + } + + // Read RW chunk pack header. + ChunkPackHeader chunk_pack_header = {}; + fread(&chunk_pack_header, sizeof(chunk_pack_header), 1, archive); + std::string chunkpack_id = chunk_pack_header.ID; + + _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. + + // Parse and unpack conditions. + bool full_archive_unpack = unpack && selected_file_index == -1; // Do a full archive unpack. + bool single_archive_unpack = unpack && selected_file_index == i; // Do a single file archive unpack. + + // Run the unpacking functions and set the file types. + // Chunk packed with Refpack compression. + if (chunkpack_id == "chunkref" && size_Vector[i] > 0) { + strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKREF"); + if (full_archive_unpack || single_archive_unpack) { + if (!chunk_decompress(archive, out_filedirectory, out_filepath)) { + fclose(archive); return RESULT{ Archive_Parse_Struct_vector , false }; + } } - - // Save start position. - uint64_t start_of_archive = _ftelli64(archive); - - // Save archive size and go back to start position. - fseek(archive, 0, SEEK_END); - uint64_t archive_size = _ftelli64(archive); - fseek(archive, start_of_archive, SEEK_SET); - - // Read archive header and convert to little endian. - fread(&archive_header, sizeof(archive_header), 1, archive); - archive_header.Signature = BigToLittleUINT(archive_header.Signature); - archive_header.HeaderVersion = BigToLittleUINT(archive_header.HeaderVersion); - archive_header.FileAmount = BigToLittleUINT(archive_header.FileAmount); - archive_header.Flags = BigToLittleUINT(archive_header.Flags); - archive_header.NamesOffset = BigToLittleUINT(archive_header.NamesOffset); - archive_header.NamesSize = BigToLittleUINT(archive_header.NamesSize); - archive_header.FolderAmount = BigToLittleUINT(archive_header.FolderAmount); - archive_header.ArchiveSize = BigToLittleUINT(archive_header.ArchiveSize); - archive_header.FatSize = BigToLittleUINT(archive_header.FatSize); - - if (archive_header.FileAmount > archive_size || archive_header.ArchiveSize > archive_size || archive_header.FatSize > archive_size) - { + } + // Chunk packed with Zlib/Deflate compression. + else if (chunkpack_id == "chunkzip" && size_Vector[i] > 0) { + strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKZIP"); + if (full_archive_unpack || single_archive_unpack) { + if (!chunk_decompress(archive, out_filedirectory, out_filepath)) { fclose(archive); return RESULT{ Archive_Parse_Struct_vector , false }; + } } - - // Set folder offset. - folders_offset = archive_header.FileAmount; - folders_offset *= archive_header.FilenameLength; - folders_offset += archive_header.NamesOffset; - folders_offset = roundUpToMultiple(folders_offset, 0x10); - archive_header.FilenameLength -= 2; - - _fseeki64(archive, 0x30, SEEK_SET); - for (size_t i = 0; i < archive_header.FileAmount; i++) - { - // Build toc_index (or whatever we would call this) - TOCIndex toc_index = {}; - toc_offset_Vector.push_back(_ftelli64(archive)); - fread(&toc_index, sizeof(toc_index), 1, archive); - toc_index.offset = BigToLittleUINT(toc_index.offset); - toc_index.compressed_size = BigToLittleUINT(toc_index.compressed_size); - toc_index.size = BigToLittleUINT(toc_index.size); - toc_index.hash1 = BigToLittleUINT(toc_index.hash1); - toc_index.hash2 = BigToLittleUINT(toc_index.hash2); - - if (toc_index.offset > archive_size || toc_index.compressed_size > archive_size || toc_index.size > archive_size) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; + } + // Chunk packed with LZX compression. + else if (chunkpack_id == "chunklzx" && size_Vector[i] > 0) { + strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKLZX"); + if (full_archive_unpack || single_archive_unpack) { + if (!chunk_decompress(archive, out_filedirectory, out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } + } + } + // If we enter here, the file is not chunk packed. + else if (size_Vector[i] > 0) { + WORD refpack_magic = 0; + fread(&refpack_magic, sizeof(WORD), 1, archive); + refpack_magic = BigToLittleUINT(refpack_magic); + _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. + + // Parse/Unpack single file compressed with refpack + if (refpack_magic == 0x10FB) { + strcpy_s(Parsed_Archive_Struct.ztype, "REFPACK"); + if (full_archive_unpack || single_archive_unpack) { + if (!unpack_refpack_file(archive, size_Vector[i], out_filedirectory, out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; } - - if (!(archive_header.Flags & FLAG_64BITHASH)) - { - _fseeki64(archive, -4, SEEK_CUR); + } + } + else { // Parse/Unpack single file without compression. + strcpy_s(Parsed_Archive_Struct.ztype, "NONE"); + if (full_archive_unpack || single_archive_unpack) { + if (!unpack_uncompressed_file(archive, size_Vector[i], out_filedirectory, out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; } - - // Push values into vector. - UINT64 OFFSET64 = toc_index.offset; - OFFSET64 = OFFSET64 << archive_header.Alignment; - offset_Vector.push_back(OFFSET64); - size_Vector.push_back(toc_index.size); + } } - - for (size_t i = 0; i < archive_header.FileAmount; i++) - { - // Read compression type. (This doesn't exist on all versions of EB V3 and you may need to figure this out some other way.) - BYTE compression_type = 0; - fread(&compression_type, sizeof(compression_type), 1, archive); - compression_type_Vector.push_back(compression_type); + } + // Parse/Unpack empty file. (any type) + else if (size_Vector[i] == 0) { + strcpy_s(Parsed_Archive_Struct.ztype, "EMPTY FILE"); + if (full_archive_unpack || single_archive_unpack) { + if (!unpack_uncompressed_file(archive, size_Vector[i], out_filedirectory, out_filepath)) { + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , false }; + } } + } - _fseeki64(archive, archive_header.NamesOffset, SEEK_SET); - for (size_t i = 0; i < archive_header.FileAmount; i++) - { - // Declare local variables. - Archive_Parse_Struct Parsed_Archive_Struct = {}; - - std::wstring wide_archiv_path = archiveName; - std::wstring directory = ParseFilePath(wide_archiv_path).first; - std::wstring out_filepath = directory; - std::wstring out_filedirectory = directory; - - // Read the folder number and convert to little endian. - WORD folder_number = 0; - fread(&folder_number, sizeof(folder_number), 1, archive); - folder_number = BigToLittleUINT(folder_number); - - // Read and build directory. (Stage 1: Filename) - std::vector name_buffer(archive_header.FilenameLength); - fread(name_buffer.data(), archive_header.FilenameLength, 1, archive); - std::string name(name_buffer.begin(), std::find(name_buffer.begin(), name_buffer.end(), '\0')); - std::string filename = name; - - DWORD next_name_offset = _ftelli64(archive); // Save the current location as next name offset location. - DWORD foldername_offset = folder_number; - foldername_offset *= archive_header.FoldernameLength; - foldername_offset += folders_offset; - - _fseeki64(archive, foldername_offset, SEEK_SET); // Seek to foldername location in archive. - - // Read and build directory. (Stage 2: directory) - std::vector folder_buffer(archive_header.FoldernameLength); - fread(folder_buffer.data(), archive_header.FoldernameLength, 1, archive); - std::string folder(folder_buffer.begin(), std::find(folder_buffer.begin(), folder_buffer.end(), '\0')); - std::string final_extracted_filepath = folder; - final_extracted_filepath += "/"; - final_extracted_filepath += name; - - _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. - - // Prepare final wide character strings. (Stage 3: Build proper path) - out_filedirectory += to_wstring(folder); - std::replace(out_filepath.begin(), out_filepath.end(), L'/', L'\\'); - std::replace(out_filedirectory.begin(), out_filedirectory.end(), L'/', L'\\'); - out_filepath += to_wstring(final_extracted_filepath); - - if (!unpack) - { - // Set Parsed_Archive struct members. - Parsed_Archive_Struct.filename = folder + name; - Parsed_Archive_Struct.file_size = size_Vector[i]; - Parsed_Archive_Struct.file_offset = offset_Vector[i]; - Parsed_Archive_Struct.toc_offset = toc_offset_Vector[i]; - } - - // Read RW chunk pack header. - ChunkPackHeader chunk_pack_header = {}; - fread(&chunk_pack_header, sizeof(chunk_pack_header), 1, archive); - std::string chunkpack_id = chunk_pack_header.ID; - - _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. - - bool full_archive_unpack = unpack && selected_file_index == -1; // Do a full archive unpack. - bool single_archive_unpack = unpack && selected_file_index == i; // Do a single file archive unpack. - - if (size_Vector[i] != 0) - { - if (chunkpack_id == "chunkref") // Chunkpacked file compressed with refpack. - { - strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKREF"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (chunk_decompress(archive, out_filedirectory, out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - } - else if (chunkpack_id == "chunkzip") // Chunkpacked file compressed with zlib. - { - strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKZIP"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (chunk_decompress(archive, out_filedirectory, out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - } - else if (chunkpack_id == "chunklzx") // Chunkpacked file compressed with lzx. - { - strcpy_s(Parsed_Archive_Struct.ztype, "CHUNKLZX"); - if (!unpack) { goto dont_unpack_loc; } - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - else //Single file (can be uncompressed or compressed with refpack) - { - WORD refpack_magic = 0; - fread(&refpack_magic, sizeof(WORD), 1, archive); - refpack_magic = BigToLittleUINT(refpack_magic); - _fseeki64(archive, offset_Vector[i], SEEK_SET); // Seek to file offset location. - if (refpack_magic == 0x10FB) // Single file compressed with refpack - { - strcpy_s(Parsed_Archive_Struct.ztype, "REFPACK"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (unpack_refpack_file(archive, size_Vector[i], out_filedirectory, out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - } - else // Single file uncompressed - { - strcpy_s(Parsed_Archive_Struct.ztype, "NONE"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (unpack_uncompressed_file(archive, size_Vector[i], out_filedirectory, out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - } - } - } - else - { - strcpy_s(Parsed_Archive_Struct.ztype, "EMPTY FILE"); - if (!unpack) - { - goto dont_unpack_loc; - } - else if (full_archive_unpack || single_archive_unpack) - { - if (unpack_empty_file(archive, size_Vector[i], out_filedirectory, out_filepath) != true) - { - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , false }; - } - } - } + // Clear buffers. + name_buffer.clear(); + name.clear(); + folder_buffer.clear(); + folder.clear(); - dont_unpack_loc: - name_buffer.clear(); - name.clear(); - folder_buffer.clear(); - folder.clear(); - Archive_Parse_Struct_vector.push_back(Parsed_Archive_Struct); - _fseeki64(archive, next_name_offset, SEEK_SET); // Seek to the saved next name location. - } + // Push to archive UI struct. + Archive_Parse_Struct_vector.push_back(Parsed_Archive_Struct); + _fseeki64(archive, next_name_offset, SEEK_SET); // Seek to the saved next name location. + } - toc_offset_Vector.clear(); - offset_Vector.clear(); - size_Vector.clear(); - compression_type_Vector.clear(); + // Clear buffers. + toc_offset_Vector.clear(); + offset_Vector.clear(); + size_Vector.clear(); + compression_type_Vector.clear(); - // Close the archive. - fclose(archive); - return RESULT{ Archive_Parse_Struct_vector , true }; - } + // Close the archive. + fclose(archive); + return RESULT{ Archive_Parse_Struct_vector , true }; + } } diff --git a/RW4ArchiveTool/Archives/Tools/EAHashes.h b/RW4ArchiveTool/Archives/Tools/EAHashes.h index 780cf64..a0953dc 100644 --- a/RW4ArchiveTool/Archives/Tools/EAHashes.h +++ b/RW4ArchiveTool/Archives/Tools/EAHashes.h @@ -72,27 +72,27 @@ namespace EAHashes { return ~seed; } + + uint32_t djb2_hash_32(const char* str) { + uint32_t hash = 5381; + int c; - uint32_t djb2_hash_32(const char* str) { - uint32_t hash = 5381; - int c; - - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; - } + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } - return hash; - } + return hash; + } - uint64_t djb2_hash_64(const char* str) { - uint64_t hash = 5381; - int c; + uint64_t djb2_hash_64(const char* str) { + uint64_t hash = 5381; + int c; - while ((c = *str++)) { - hash = ((hash << 5) + hash) + c; - } + while ((c = *str++)) { + hash = ((hash << 5) + hash) + c; + } - return hash; - } + return hash; + } } diff --git a/RW4ArchiveTool/Archives/Tools/IoTools.h b/RW4ArchiveTool/Archives/Tools/IoTools.h index 8a799c0..2594c4c 100644 --- a/RW4ArchiveTool/Archives/Tools/IoTools.h +++ b/RW4ArchiveTool/Archives/Tools/IoTools.h @@ -2,136 +2,136 @@ namespace IoTools { - bool create_file(FILE* file, std::wstring out_path) + bool create_file(FILE* file, std::wstring out_path) + { + // Write to file. + if (_wfopen_s(&file, out_path.c_str(), L"wb+") == 0) { - // Write to file. - if (_wfopen_s(&file, out_path.c_str(), L"wb+") == 0) - { - if (file != nullptr) { - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } - } - else { - OutputDebugStringA("Error opening file.\n"); return false; - } - - return true; + if (file != nullptr) { + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } + } + else { + OutputDebugStringA("Error opening file.\n"); return false; } - bool create_file(FILE* file, std::string out_path) - { - // Write to file. - if (fopen_s(&file, out_path.c_str(), "wb+") == 0) - { - if (file != nullptr) { - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } - } - else { - OutputDebugStringA("Error opening file.\n"); return false; - } + return true; + } - return true; + bool create_file(FILE* file, std::string out_path) + { + // Write to file. + if (fopen_s(&file, out_path.c_str(), "wb+") == 0) + { + if (file != nullptr) { + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } + } + else { + OutputDebugStringA("Error opening file.\n"); return false; } - bool write_file(FILE* file, std::wstring out_path, char* buffer, uint64_t size) + return true; + } + + bool write_file(FILE* file, std::wstring out_path, char* buffer, uint64_t size) + { + // Write to file. + if (_wfopen_s(&file, out_path.c_str(), L"wb+") == 0) { - // Write to file. - if (_wfopen_s(&file, out_path.c_str(), L"wb+") == 0) - { - if (file != nullptr) { - size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); - if (bytesWritten != size) { - OutputDebugStringA("Error writing to file.\n"); return false; - } - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } + if (file != nullptr) { + size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); + if (bytesWritten != size) { + OutputDebugStringA("Error writing to file.\n"); return false; } - else { - OutputDebugStringA("Error opening file.\n"); return false; - } - - return true; + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } + } + else { + OutputDebugStringA("Error opening file.\n"); return false; } - bool write_file(FILE* file, std::string out_path, char* buffer, uint64_t size) + return true; + } + + bool write_file(FILE* file, std::string out_path, char* buffer, uint64_t size) + { + // Write to file. + if (fopen_s(&file, out_path.c_str(), "wb+") == 0) { - // Write to file. - if (fopen_s(&file, out_path.c_str(), "wb+") == 0) - { - if (file != nullptr) { - size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); - if (bytesWritten != size) { - OutputDebugStringA("Error writing to file.\n"); return false; - } - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } - } - else { - OutputDebugStringA("Error opening file.\n"); return false; + if (file != nullptr) { + size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); + if (bytesWritten != size) { + OutputDebugStringA("Error writing to file.\n"); return false; } - - return true; + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } + } + else { + OutputDebugStringA("Error opening file.\n"); return false; } - bool append_file(FILE* file, std::wstring out_path, char* buffer, uint64_t size) + return true; + } + + bool append_file(FILE* file, std::wstring out_path, char* buffer, uint64_t size) + { + // Write to file. + if (_wfopen_s(&file, out_path.c_str(), L"ab+") == 0) { - // Write to file. - if (_wfopen_s(&file, out_path.c_str(), L"ab+") == 0) - { - if (file != nullptr) { - size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); - if (bytesWritten != size) { - OutputDebugStringA("Error writing to file.\n"); return false; - } - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } - } - else { - OutputDebugStringA("Error opening file.\n"); return false; + if (file != nullptr) { + size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); + if (bytesWritten != size) { + OutputDebugStringA("Error writing to file.\n"); return false; } - - return true; + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } } + else { + OutputDebugStringA("Error opening file.\n"); return false; + } + + return true; + } - bool append_file(FILE* file, std::string out_path, char* buffer, uint64_t size) + bool append_file(FILE* file, std::string out_path, char* buffer, uint64_t size) + { + // Write to file. + if (fopen_s(&file, out_path.c_str(), "ab+") == 0) { - // Write to file. - if (fopen_s(&file, out_path.c_str(), "ab+") == 0) - { - if (file != nullptr) { - size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); - if (bytesWritten != size) { - OutputDebugStringA("Error writing to file.\n"); return false; - } - fclose(file); - } - else { - OutputDebugStringA("Error opening file for append.\n"); return false; - } - } - else { - OutputDebugStringA("Error opening file.\n"); return false; + if (file != nullptr) { + size_t bytesWritten = fwrite(buffer, sizeof(char), size, file); + if (bytesWritten != size) { + OutputDebugStringA("Error writing to file.\n"); return false; } - - return true; + fclose(file); + } + else { + OutputDebugStringA("Error opening file for append.\n"); return false; + } + } + else { + OutputDebugStringA("Error opening file.\n"); return false; } + return true; + } + diff --git a/RW4ArchiveTool/Archives/Tools/big_endian_tools.h b/RW4ArchiveTool/Archives/Tools/big_endian_tools.h index be262de..10cfdff 100644 --- a/RW4ArchiveTool/Archives/Tools/big_endian_tools.h +++ b/RW4ArchiveTool/Archives/Tools/big_endian_tools.h @@ -5,101 +5,101 @@ struct UINTTypeSelector; template struct UINTTypeSelector { - using Type = uint16_t; + using Type = uint16_t; }; template struct UINTTypeSelector { - using Type = uint32_t; + using Type = uint32_t; }; template struct UINTTypeSelector { - using Type = uint64_t; + using Type = uint64_t; }; template typename UINTTypeSelector::Type BigToLittleUINT(T input) { - using ReturnType = typename UINTTypeSelector::Type; - - if constexpr (sizeof(T) == 2) { - return static_cast((input & 0xFF) << 8) | ((input >> 8) & 0xFF); - } - else if constexpr (sizeof(T) == 4) { - return static_cast((input & 0xFF) << 24) | (((input >> 8) & 0xFF) << 16) | - (((input >> 16) & 0xFF) << 8) | ((input >> 24) & 0xFF);; - } - else if constexpr (sizeof(T) == 8) { - return static_cast((input & 0xFFULL) << 56) | (((input >> 8) & 0xFFULL) << 48) | - (((input >> 16) & 0xFFULL) << 40) | (((input >> 24) & 0xFFULL) << 32) | - (((input >> 32) & 0xFFULL) << 24) | (((input >> 40) & 0xFFULL) << 16) | - (((input >> 48) & 0xFFULL) << 8) | ((input >> 56) & 0xFFULL);; - } - else { - // Unsupported size - static_assert(sizeof(T) == 0, "Unsupported integer size"); - } + using ReturnType = typename UINTTypeSelector::Type; + + if constexpr (sizeof(T) == 2) { + return static_cast((input & 0xFF) << 8) | ((input >> 8) & 0xFF); + } + else if constexpr (sizeof(T) == 4) { + return static_cast((input & 0xFF) << 24) | (((input >> 8) & 0xFF) << 16) | + (((input >> 16) & 0xFF) << 8) | ((input >> 24) & 0xFF);; + } + else if constexpr (sizeof(T) == 8) { + return static_cast((input & 0xFFULL) << 56) | (((input >> 8) & 0xFFULL) << 48) | + (((input >> 16) & 0xFFULL) << 40) | (((input >> 24) & 0xFFULL) << 32) | + (((input >> 32) & 0xFFULL) << 24) | (((input >> 40) & 0xFFULL) << 16) | + (((input >> 48) & 0xFFULL) << 8) | ((input >> 56) & 0xFFULL);; + } + else { + // Unsupported size + static_assert(sizeof(T) == 0, "Unsupported integer size"); + } } // Make a hexadecimal string from a hexadecimal array. void bytearray_to_hexstring(const unsigned char* byteArray, size_t byteArraySize, char* hexString) { - for (size_t i = 0; i < byteArraySize; ++i) { - snprintf(&hexString[i * 2], 3, "%02X", byteArray[i]); - } + for (size_t i = 0; i < byteArraySize; ++i) { + snprintf(&hexString[i * 2], 3, "%02X", byteArray[i]); + } } uint64_t roundUpToMultiple(uint64_t value, uint64_t multiple) { - if (multiple == 0) { - // Avoid division by zero - return value; - } + if (multiple == 0) { + // Avoid division by zero + return value; + } - // Calculate the remainder - uint64_t remainder = value % multiple; + // Calculate the remainder + uint64_t remainder = value % multiple; - // If the remainder is non-zero, round up - if (remainder != 0) { - value += (multiple - remainder); - } + // If the remainder is non-zero, round up + if (remainder != 0) { + value += (multiple - remainder); + } - return value; + return value; } bool CreateDirectoryRecursively(const wchar_t* path) { - // Attempt to create the directory - if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { - return true; - } - - // If creation failed and the error is because the directory already exists, return true - if (GetLastError() == ERROR_ALREADY_EXISTS) { - return true; - } - - // If the error is because of missing parent directories, create them recursively - if (GetLastError() == ERROR_PATH_NOT_FOUND) { - size_t pos = std::wstring(path).find_last_of(L"\\/"); - if (pos != std::wstring::npos) { - std::wstring parentPath = std::wstring(path).substr(0, pos); - if (!CreateDirectoryRecursively(parentPath.c_str())) { - return false; - } - - // Retry creating the directory after creating the parent directories - return CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS; - } + // Attempt to create the directory + if (CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS) { + return true; + } + + // If creation failed and the error is because the directory already exists, return true + if (GetLastError() == ERROR_ALREADY_EXISTS) { + return true; + } + + // If the error is because of missing parent directories, create them recursively + if (GetLastError() == ERROR_PATH_NOT_FOUND) { + size_t pos = std::wstring(path).find_last_of(L"\\/"); + if (pos != std::wstring::npos) { + std::wstring parentPath = std::wstring(path).substr(0, pos); + if (!CreateDirectoryRecursively(parentPath.c_str())) { + return false; + } + + // Retry creating the directory after creating the parent directories + return CreateDirectory(path, NULL) || GetLastError() == ERROR_ALREADY_EXISTS; } + } return false; } uint64_t hash(const char* str) // Hash used for big archives. { - uint64_t hash = 5381; - int c; + uint64_t hash = 5381; + int c; - while (c = *str++) - hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ + while (c = *str++) + hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ - return hash; + return hash; } \ No newline at end of file diff --git a/RW4ArchiveTool/RW4ArchiveTool.vcxproj b/RW4ArchiveTool/RW4ArchiveTool.vcxproj index af64d72..d602030 100644 --- a/RW4ArchiveTool/RW4ArchiveTool.vcxproj +++ b/RW4ArchiveTool/RW4ArchiveTool.vcxproj @@ -112,13 +112,13 @@ _DEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp20 - $(ProjectDir)Archives\Tools;$(ProjectDir)Archives\Compression;%(AdditionalIncludeDirectories) + $(ProjectDir)Archives\Compression\lzx\wimlib\include;$(ProjectDir)Archives\Tools;$(ProjectDir)Archives\Compression;%(AdditionalIncludeDirectories) Windows true Comctl32.lib;shell32.lib;shlwapi.lib;zlibstat.lib;%(AdditionalDependencies) - $(ProjectDir)Archives\Compression\Zlib\Libraries\ZlibStatDebug;%(AdditionalLibraryDirectories) + $(ProjectDir)Archives\Compression\lzx\wimlib\lib;$(ProjectDir)Archives\Compression\Zlib\Libraries\ZlibStatDebug;%(AdditionalLibraryDirectories) @@ -130,7 +130,7 @@ NDEBUG;_WINDOWS;%(PreprocessorDefinitions) true stdcpp20 - $(ProjectDir)Archives\Tools;$(ProjectDir)Archives\Compression;%(AdditionalIncludeDirectories) + $(ProjectDir)Archives\Compression\lzx\wimlib\include;$(ProjectDir)Archives\Tools;$(ProjectDir)Archives\Compression;%(AdditionalIncludeDirectories) Windows @@ -138,10 +138,11 @@ true true Comctl32.lib;shell32.lib;shlwapi.lib;zlibstat.lib;%(AdditionalDependencies) - $(ProjectDir)Archives\Compression\Zlib\Libraries\ZlibStatRelease;%(AdditionalLibraryDirectories) + $(ProjectDir)Archives\Compression\lzx\wimlib\lib;$(ProjectDir)Archives\Compression\Zlib\Libraries\ZlibStatRelease;%(AdditionalLibraryDirectories) + @@ -167,6 +168,7 @@ + diff --git a/RW4ArchiveTool/RW4ArchiveTool.vcxproj.filters b/RW4ArchiveTool/RW4ArchiveTool.vcxproj.filters index a7306bb..7a69d1f 100644 --- a/RW4ArchiveTool/RW4ArchiveTool.vcxproj.filters +++ b/RW4ArchiveTool/RW4ArchiveTool.vcxproj.filters @@ -70,6 +70,7 @@ Headers\Archives\Tools + @@ -141,5 +142,6 @@ Source + \ No newline at end of file diff --git a/RW4ArchiveTool/Resources/RW4ArchiveTool.aps b/RW4ArchiveTool/Resources/RW4ArchiveTool.aps index 38d4a1315d73e861ebe90ca128bf692b4b27de98..e105124b7914807230b989ae5443d5e5d02528f6 100644 GIT binary patch delta 43 ucmaDeo$bwZwuUW?7R{V244e!gz_Q)3nbDk)(PVpN3!^)TKG@2r%?1DwG7Dz_ delta 43 ucmaDeo$bwZwuUW?7R{VY44e!gz_i`5nbDk)(P(>R3!^)TKG@2r%?1DvnhRe5 diff --git a/RW4ArchiveTool/Resources/RW4ArchiveTool.rc b/RW4ArchiveTool/Resources/RW4ArchiveTool.rc index 2a373dfb463ca9922f4056e666c287b6ecdab155..2fbebbd8576d46417bda646e0d5fa5adc0932c37 100644 GIT binary patch delta 57 zcmaD8`6hBhk}{*o GetSelectedPaths(HWND hwndListView) { - std::vector paths; + std::vector paths; - int index = -1; - while ((index = ListView_GetNextItem(hwndListView, index, LVNI_SELECTED)) != -1) { - paths.push_back(GetPathFromListView(hwndListView, index)); - } + int index = -1; + while ((index = ListView_GetNextItem(hwndListView, index, LVNI_SELECTED)) != -1) { + paths.push_back(GetPathFromListView(hwndListView, index)); + } - return paths; + return paths; } DWORD GetFileSizeFromOpenFileName(HWND& hwnd, const std::wstring& directory, const std::wstring& filename) { - std::wstring filePath = directory + filename; - - HANDLE hFile = CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (hFile == INVALID_HANDLE_VALUE) { - DWORD error = GetLastError(); - LPVOID errorMsg; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error, - 0, // Default language - (LPWSTR)&errorMsg, - 0, - NULL - ); - MessageBox(hwnd, (LPCWSTR)errorMsg, TEXT("Error opening file"), MB_OK | MB_ICONERROR); - LocalFree(errorMsg); // Free the buffer allocated by FormatMessage - return 0; - } - - DWORD fileSize = GetFileSize(hFile, NULL); - if (fileSize == INVALID_FILE_SIZE) { - DWORD error = GetLastError(); - LPVOID errorMsg; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error, - 0, // Default language - (LPWSTR)&errorMsg, - 0, - NULL - ); - MessageBox(hwnd, (LPCWSTR)errorMsg, TEXT("Error getting file size"), MB_OK | MB_ICONERROR); - LocalFree(errorMsg); // Free the buffer allocated by FormatMessage - CloseHandle(hFile); // Close the file handle - return 0; - } - + std::wstring filePath = directory + filename; + + HANDLE hFile = CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) { + DWORD error = GetLastError(); + LPVOID errorMsg; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + 0, // Default language + (LPWSTR)&errorMsg, + 0, + NULL + ); + MessageBox(hwnd, (LPCWSTR)errorMsg, TEXT("Error opening file"), MB_OK | MB_ICONERROR); + LocalFree(errorMsg); // Free the buffer allocated by FormatMessage + return 0; + } + + DWORD fileSize = GetFileSize(hFile, NULL); + if (fileSize == INVALID_FILE_SIZE) { + DWORD error = GetLastError(); + LPVOID errorMsg; + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + 0, // Default language + (LPWSTR)&errorMsg, + 0, + NULL + ); + MessageBox(hwnd, (LPCWSTR)errorMsg, TEXT("Error getting file size"), MB_OK | MB_ICONERROR); + LocalFree(errorMsg); // Free the buffer allocated by FormatMessage CloseHandle(hFile); // Close the file handle + return 0; + } - return fileSize; + CloseHandle(hFile); // Close the file handle + + return fileSize; } int CountSelectedFiles(const TCHAR* buffer) { - int count = 0; + int count = 0; - while (*buffer) { - ++count; + while (*buffer) { + ++count; - // Move to the next null-terminated string - buffer += _tcslen(buffer) + 1; - } + // Move to the next null-terminated string + buffer += _tcslen(buffer) + 1; + } - return count; + return count; } // Function to update the ListView2 with file information void UpdateFileView(HWND hwnd_list, std::vector fileVector) { - // Clear existing items in the ListView - ListView_DeleteAllItems(hwnd_list); - - // Set redraw to false to save many instuctions. - SendMessage(hwnd_list, WM_SETREDRAW, FALSE, 0); - - // Iterate through the vector and add each struct's information to the ListView - LVITEM lvItem; - memset(&lvItem, 0, sizeof(LVITEM)); - lvItem.mask = LVIF_TEXT; - - // Set the total number of items beforehand - SendMessage(hwnd_list, LVM_SETITEMCOUNT, fileVector.size(), 0); - for (size_t i = 0; i < fileVector.size(); ++i) { - // Add the index to the first column - lvItem.iItem = i; - lvItem.iSubItem = 0; - lvItem.pszText = LPSTR_TEXTCALLBACK; - ListView_InsertItem(hwnd_list, &lvItem); - - std::wstring WideFileName = to_wstring(fileVector[i].filename); - std::wstring file_size = std::to_wstring(fileVector[i].file_size); - std::wstring file_index = std::to_wstring(i); - std::wstring file_offset = std::to_wstring(fileVector[i].file_offset); - std::wstring toc_offset = std::to_wstring(fileVector[i].toc_offset); - std::wstring WideZtype = to_wstring(fileVector[i].ztype); - - // Add other columns - ListView_SetItemText(hwnd_list, i, 0, const_cast(file_index.c_str())); - ListView_SetItemText(hwnd_list, i, 1, const_cast(WideFileName.c_str())); - ListView_SetItemText(hwnd_list, i, 2, const_cast(file_size.c_str())); - ListView_SetItemText(hwnd_list, i, 3, const_cast(file_offset.c_str())); - ListView_SetItemText(hwnd_list, i, 4, const_cast(WideZtype.c_str())); - ListView_SetItemText(hwnd_list, i, 5, const_cast(toc_offset.c_str())); - } - - // Set redraw to true after adding items. - SendMessage(hwnd_list, WM_SETREDRAW, TRUE, 0); - InvalidateRect(hwnd_list, NULL, TRUE); + // Clear existing items in the ListView + ListView_DeleteAllItems(hwnd_list); + + // Set redraw to false to save many instuctions. + SendMessage(hwnd_list, WM_SETREDRAW, FALSE, 0); + + // Iterate through the vector and add each struct's information to the ListView + LVITEM lvItem; + memset(&lvItem, 0, sizeof(LVITEM)); + lvItem.mask = LVIF_TEXT; + + // Set the total number of items beforehand + SendMessage(hwnd_list, LVM_SETITEMCOUNT, fileVector.size(), 0); + for (size_t i = 0; i < fileVector.size(); ++i) { + // Add the index to the first column + lvItem.iItem = i; + lvItem.iSubItem = 0; + lvItem.pszText = LPSTR_TEXTCALLBACK; + ListView_InsertItem(hwnd_list, &lvItem); + + std::wstring WideFileName = to_wstring(fileVector[i].filename); + std::wstring file_size = std::to_wstring(fileVector[i].file_size); + std::wstring file_index = std::to_wstring(i); + std::wstring file_offset = std::to_wstring(fileVector[i].file_offset); + std::wstring toc_offset = std::to_wstring(fileVector[i].toc_offset); + std::wstring WideZtype = to_wstring(fileVector[i].ztype); + + // Add other columns + ListView_SetItemText(hwnd_list, i, 0, const_cast(file_index.c_str())); + ListView_SetItemText(hwnd_list, i, 1, const_cast(WideFileName.c_str())); + ListView_SetItemText(hwnd_list, i, 2, const_cast(file_size.c_str())); + ListView_SetItemText(hwnd_list, i, 3, const_cast(file_offset.c_str())); + ListView_SetItemText(hwnd_list, i, 4, const_cast(WideZtype.c_str())); + ListView_SetItemText(hwnd_list, i, 5, const_cast(toc_offset.c_str())); + } + + // Set redraw to true after adding items. + SendMessage(hwnd_list, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwnd_list, NULL, TRUE); } // Function to update the ListView1 with Archive information void UpdateArchiveView(HWND hwnd, wchar_t* file) { - // Update the list view - HWND hListView = GetDlgItem(hwnd, ID_LIST_VIEW); - ListView_DeleteAllItems(hwndListView); - ListView_DeleteAllItems(hwndListView2); + // Update the list view + HWND hListView = GetDlgItem(hwnd, ID_LIST_VIEW); + ListView_DeleteAllItems(hwndListView); + ListView_DeleteAllItems(hwndListView2); + + // Set redraw to false to save many instuctions. + SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0); + + // Parse the selected files and update the list view + std::pair> selected_archives; + int numSelectedFiles = CountSelectedFiles(file); + if (numSelectedFiles == 1) + { + selected_archives = ParseFilePath(file); // Handle single selection archive paths. + } + else + { + selected_archives = ParseMultiFilePath(file); // Handle multiple selection archive paths. + } + + // Iterate through the vector and add each struct's information to the ListView + LVITEM lvItem; + memset(&lvItem, 0, sizeof(LVITEM)); + lvItem.mask = LVIF_TEXT; + + for (size_t i = 0; i < selected_archives.second.size(); ++i) + { + // Add the index to the first column + lvItem.iItem = i; + lvItem.iSubItem = 0; + lvItem.pszText = LPSTR_TEXTCALLBACK; + ListView_InsertItem(hwndListView, &lvItem); - // Set redraw to false to save many instuctions. - SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0); + // Create file path wstring. + std::wstring FullPath = selected_archives.first.c_str(); + FullPath += selected_archives.second[i].c_str(); - // Parse the selected files and update the list view - std::pair> selected_archives; - int numSelectedFiles = CountSelectedFiles(file); - if (numSelectedFiles == 1) + // Get information + std::wstring archive_size = std::to_wstring(GetFileSizeFromOpenFileName(hwnd, selected_archives.first, selected_archives.second[i])); + + // Fill listview with information. + switch (magic::magic_parser(FullPath.c_str())) { - selected_archives = ParseFilePath(file); // Handle single selection archive paths. - } - else + case BIG_EB: + ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); + break; + case BIG4: + ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); + break; + case ARENA: + ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); + break; + case SFIL: { - selected_archives = ParseMultiFilePath(file); // Handle multiple selection archive paths. + SFArchiveHeaderNotDynamic parsed_sf_header = sf::parse_sfa_header(FullPath.c_str()); + std::wstring archive_streamformat = std::to_wstring(parsed_sf_header.StreamFormat); + std::wstring archive_Version = std::to_wstring(parsed_sf_header.Version); + std::wstring archive_NumCollections = std::to_wstring(parsed_sf_header.NumCollections); + ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); + ListView_SetItemText(hwndListView, i, 3, const_cast(archive_streamformat.c_str())); + ListView_SetItemText(hwndListView, i, 4, const_cast(archive_Version.c_str())); + ListView_SetItemText(hwndListView, i, 5, const_cast(archive_NumCollections.c_str())); + break; } - - // Iterate through the vector and add each struct's information to the ListView - LVITEM lvItem; - memset(&lvItem, 0, sizeof(LVITEM)); - lvItem.mask = LVIF_TEXT; - - for (size_t i = 0; i < selected_archives.second.size(); ++i) - { - // Add the index to the first column - lvItem.iItem = i; - lvItem.iSubItem = 0; - lvItem.pszText = LPSTR_TEXTCALLBACK; - ListView_InsertItem(hwndListView, &lvItem); - - // Create file path wstring. - std::wstring FullPath = selected_archives.first.c_str(); - FullPath += selected_archives.second[i].c_str(); - - // Get information - std::wstring archive_size = std::to_wstring(GetFileSizeFromOpenFileName(hwnd, selected_archives.first, selected_archives.second[i])); - - // Fill listview with information. - switch (magic::magic_parser(FullPath.c_str())) - { - case BIG_EB: - ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2 , const_cast(selected_archives.first.c_str())); - break; - case BIG4: - ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); - break; - case ARENA: - ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); - break; - case SFIL: - { - SFArchiveHeaderNotDynamic parsed_sf_header = sf::parse_sfa_header(FullPath.c_str()); - std::wstring archive_streamformat = std::to_wstring(parsed_sf_header.StreamFormat); - std::wstring archive_Version = std::to_wstring(parsed_sf_header.Version); - std::wstring archive_NumCollections = std::to_wstring(parsed_sf_header.NumCollections); - ListView_SetItemText(hwndListView, i, 0, const_cast(selected_archives.second[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(selected_archives.first.c_str())); - ListView_SetItemText(hwndListView, i, 3, const_cast(archive_streamformat.c_str())); - ListView_SetItemText(hwndListView, i, 4, const_cast(archive_Version.c_str())); - ListView_SetItemText(hwndListView, i, 5, const_cast(archive_NumCollections.c_str())); - break; - } - case UNKNOWNARCHIVE: - continue; - break; - default: - break; - } + case UNKNOWNARCHIVE: + continue; + break; + default: + break; } - // Set redraw to true after adding items. - SendMessage(hwndListView, WM_SETREDRAW, TRUE, 0); - InvalidateRect(hwndListView, NULL, TRUE); + } + // Set redraw to true after adding items. + SendMessage(hwndListView, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwndListView, NULL, TRUE); } bool IsDirectoryPath(const std::wstring& path) { - if (path.empty()) { - return false; - } + if (path.empty()) { + return false; + } - // Check if the path ends with a directory separator - wchar_t lastChar = path.back(); - if (lastChar == L'\\' || lastChar == L'/') { - return true; - } + // Check if the path ends with a directory separator + wchar_t lastChar = path.back(); + if (lastChar == L'\\' || lastChar == L'/') { + return true; + } - // If the path ends with a digit, consider it as a directory path without a filename - size_t lastSeparator = path.find_last_of(L"\\/"); - if (lastSeparator != std::wstring::npos && isdigit(path.back())) { - return true; - } + // If the path ends with a digit, consider it as a directory path without a filename + size_t lastSeparator = path.find_last_of(L"\\/"); + if (lastSeparator != std::wstring::npos && isdigit(path.back())) { + return true; + } - return false; + return false; } // Subclass procedure for a specific child window LRESULT CALLBACK SubclassListViewProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { - switch (message) - { - case WM_DROPFILES: - { - // Delete old listview items. - ListView_DeleteAllItems(hwndListView); - ListView_DeleteAllItems(hwndListView2); - - // Set redraw to false to save many instuctions. - SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0); + switch (message) + { + case WM_DROPFILES: + { + // Delete old listview items. + ListView_DeleteAllItems(hwndListView); + ListView_DeleteAllItems(hwndListView2); - HDROP hDrop = (HDROP)wParam; + // Set redraw to false to save many instuctions. + SendMessage(hwndListView, WM_SETREDRAW, FALSE, 0); - // Determine the number of files dropped - UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); + HDROP hDrop = (HDROP)wParam; - // Vectors to store filenames and directories - std::vector filenames; - std::vector directories; + // Determine the number of files dropped + UINT fileCount = DragQueryFile(hDrop, 0xFFFFFFFF, NULL, 0); - // Process each dropped file - for (UINT i = 0; i < fileCount; ++i) - { - // Get the size of the buffer needed to store the file path - UINT bufferSize = DragQueryFile(hDrop, i, NULL, 0) + 1; - - // Allocate a buffer to store the file path - std::vector filePathBuffer(bufferSize, L'\0'); - - // Get the file path - DragQueryFile(hDrop, i, filePathBuffer.data(), bufferSize); - - // Check if the file has a .psf extension - std::wstring filePath(filePathBuffer.data()); - size_t lastDot = filePath.find_last_of(L"."); - - if (lastDot != std::wstring::npos) - { - std::wstring fileExtension = filePath.substr(lastDot + 1); - - // Check if the file extension is ".psf" - if ( - fileExtension != L"psf" - && fileExtension != L"wsf" - && fileExtension != L"xsf" - && fileExtension != L"big" - && fileExtension != L"rx2" - && fileExtension != L"psg" - && fileExtension != L"rg2" - && fileExtension != L"rpsgl" - ) - { - // Skip files with invalid extension - continue; - } - } - else - { - // If no separator found, assume the whole path is the filename - continue; - } - - // Split the file path into filename and directory - size_t lastSeparator = filePath.find_last_of(L"\\"); - - if (lastSeparator != std::wstring::npos) - { - std::wstring directory = filePath.substr(0, lastSeparator + 1); - std::wstring filename = filePath.substr(lastSeparator + 1); - - // Add the filename and directory to the vectors - filenames.push_back(filename); - directories.push_back(directory); - } - else - { - // If no separator found, assume the whole path is the filename - filenames.push_back(filePath); - directories.push_back(L""); - } - } + // Vectors to store filenames and directories + std::vector filenames; + std::vector directories; - - // Display the filenames and directories - for (size_t i = 0; i < filenames.size(); ++i) + // Process each dropped file + for (UINT i = 0; i < fileCount; ++i) + { + // Get the size of the buffer needed to store the file path + UINT bufferSize = DragQueryFile(hDrop, i, NULL, 0) + 1; + + // Allocate a buffer to store the file path + std::vector filePathBuffer(bufferSize, L'\0'); + + // Get the file path + DragQueryFile(hDrop, i, filePathBuffer.data(), bufferSize); + + // Check if the file has a .psf extension + std::wstring filePath(filePathBuffer.data()); + size_t lastDot = filePath.find_last_of(L"."); + + if (lastDot != std::wstring::npos) + { + std::wstring fileExtension = filePath.substr(lastDot + 1); + + // Check if the file extension is valid + if ( + fileExtension != L"psf" + && fileExtension != L"wsf" + && fileExtension != L"xsf" + && fileExtension != L"big" + && fileExtension != L"viv" + && fileExtension != L"rx2" + && fileExtension != L"psg" + && fileExtension != L"rg2" + && fileExtension != L"rpsgl" + ) { - // Iterate through the vector and add each struct's information to the ListView - LVITEM lvItem; - memset(&lvItem, 0, sizeof(LVITEM)); - lvItem.mask = LVIF_TEXT; - - // Add the index to the first column - lvItem.iItem = i; - lvItem.iSubItem = 0; - lvItem.pszText = LPSTR_TEXTCALLBACK; - ListView_InsertItem(hwndListView, &lvItem); - - // Create file path wstring. - std::wstring FullPath = directories[i].c_str(); - FullPath += filenames[i].c_str(); - - // Get information - std::wstring archive_size = std::to_wstring(GetFileSizeFromOpenFileName(hwnd, directories[i].c_str(), filenames[i].c_str())); - - switch (magic::magic_parser(FullPath.c_str())) - { - case BIG_EB: - ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); - break; - case BIG4: - ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); - break; - case ARENA: - ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); - break; - case SFIL: - { - SFArchiveHeaderNotDynamic parsed_sf_header = sf::parse_sfa_header(FullPath.c_str()); - std::wstring archive_streamformat = std::to_wstring(parsed_sf_header.StreamFormat); - std::wstring archive_Version = std::to_wstring(parsed_sf_header.Version); - std::wstring archive_NumCollections = std::to_wstring(parsed_sf_header.NumCollections); - ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); - ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); - ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); - ListView_SetItemText(hwndListView, i, 3, const_cast(archive_streamformat.c_str())); - ListView_SetItemText(hwndListView, i, 4, const_cast(archive_Version.c_str())); - ListView_SetItemText(hwndListView, i, 5, const_cast(archive_NumCollections.c_str())); - break; - } - case UNKNOWNARCHIVE: - continue; - break; - default: - break; - } + // Skip files with invalid extension + continue; } - - // Set redraw to true after adding items. - SendMessage(hwndListView, WM_SETREDRAW, TRUE, 0); - InvalidateRect(hwndListView, NULL, TRUE); - DragFinish(hDrop); - return 0; + } + else + { + // If no separator found, assume the whole path is the filename + continue; + } + + // Split the file path into filename and directory + size_t lastSeparator = filePath.find_last_of(L"\\"); + + if (lastSeparator != std::wstring::npos) + { + std::wstring directory = filePath.substr(0, lastSeparator + 1); + std::wstring filename = filePath.substr(lastSeparator + 1); + + // Add the filename and directory to the vectors + filenames.push_back(filename); + directories.push_back(directory); + } + else + { + // If no separator found, assume the whole path is the filename + filenames.push_back(filePath); + directories.push_back(L""); + } } - default: - // Call the original window procedure for unhandled messages - return DefSubclassProc(hwnd, message, wParam, lParam); + + + // Display the filenames and directories + for (size_t i = 0; i < filenames.size(); ++i) + { + // Iterate through the vector and add each struct's information to the ListView + LVITEM lvItem; + memset(&lvItem, 0, sizeof(LVITEM)); + lvItem.mask = LVIF_TEXT; + + // Add the index to the first column + lvItem.iItem = i; + lvItem.iSubItem = 0; + lvItem.pszText = LPSTR_TEXTCALLBACK; + ListView_InsertItem(hwndListView, &lvItem); + + // Create file path wstring. + std::wstring FullPath = directories[i].c_str(); + FullPath += filenames[i].c_str(); + + // Get information + std::wstring archive_size = std::to_wstring(GetFileSizeFromOpenFileName(hwnd, directories[i].c_str(), filenames[i].c_str())); + + switch (magic::magic_parser(FullPath.c_str())) + { + case BIG_EB: + ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); + break; + case BIG4: + ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); + break; + case ARENA: + ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); + break; + case SFIL: + { + SFArchiveHeaderNotDynamic parsed_sf_header = sf::parse_sfa_header(FullPath.c_str()); + std::wstring archive_streamformat = std::to_wstring(parsed_sf_header.StreamFormat); + std::wstring archive_Version = std::to_wstring(parsed_sf_header.Version); + std::wstring archive_NumCollections = std::to_wstring(parsed_sf_header.NumCollections); + ListView_SetItemText(hwndListView, i, 0, const_cast(filenames[i].c_str())); + ListView_SetItemText(hwndListView, i, 1, const_cast(archive_size.c_str())); + ListView_SetItemText(hwndListView, i, 2, const_cast(directories[i].c_str())); + ListView_SetItemText(hwndListView, i, 3, const_cast(archive_streamformat.c_str())); + ListView_SetItemText(hwndListView, i, 4, const_cast(archive_Version.c_str())); + ListView_SetItemText(hwndListView, i, 5, const_cast(archive_NumCollections.c_str())); + break; + } + case UNKNOWNARCHIVE: + continue; + break; + default: + break; + } } + + // Set redraw to true after adding items. + SendMessage(hwndListView, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwndListView, NULL, TRUE); + DragFinish(hDrop); + return 0; + } + default: + // Call the original window procedure for unhandled messages + return DefSubclassProc(hwnd, message, wParam, lParam); + } } // Subclass a ListView control void SubclassListView(HWND hwndListView) { - SetWindowSubclass(hwndListView, SubclassListViewProc, 1, 0); + SetWindowSubclass(hwndListView, SubclassListViewProc, 1, 0); } // Function to open a file dialog and load the file into the buffer void OpenFileAndLoadBuffer(HWND hwnd) { - OPENFILENAME ofn = { sizeof ofn }; - wchar_t file[10240] = {}; - file[0] = '\0'; - ZeroMemory(&ofn, sizeof(ofn)); - ofn.lStructSize = sizeof(ofn); - ofn.hwndOwner = hwnd; - ofn.lpstrFile = file; - ofn.nMaxFile = 10240; - ofn.lpstrFilter = TEXT("EA Skate Archives\0*.xsf;*.psf;*.wsf;*.big;*.rx2;*.psg;*.rg2;*.rpsgl\0\0"); - ofn.nFilterIndex = 1; - ofn.lpstrFileTitle = NULL; - ofn.nMaxFileTitle = 0; - ofn.lpstrInitialDir = NULL; - ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; - - // Display the Open dialog box. - if (GetOpenFileName(&ofn) == TRUE) - { - UpdateArchiveView(hwnd, file); - } + OPENFILENAME ofn = { sizeof ofn }; + wchar_t file[10240] = {}; + file[0] = '\0'; + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = hwnd; + ofn.lpstrFile = file; + ofn.nMaxFile = 10240; + ofn.lpstrFilter = TEXT("EA Skate Archives\0*.xsf;*.psf;*.wsf;*.big;*.viv;*.rx2;*.psg;*.rg2;*.rpsgl\0\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT | OFN_EXPLORER; + + // Display the Open dialog box. + if (GetOpenFileName(&ofn) == TRUE) + { + UpdateArchiveView(hwnd, file); + } } std::vector EnumerateFolder(IShellFolder* folder) { - std::vector filepath_wstring_vector = {}; - std::stack folderStack; + std::vector filepath_wstring_vector = {}; + std::stack folderStack; - folderStack.push(folder); + folderStack.push(folder); - while (!folderStack.empty()) { - IShellFolder* currentFolder = folderStack.top(); - folderStack.pop(); + while (!folderStack.empty()) { + IShellFolder* currentFolder = folderStack.top(); + folderStack.pop(); - IEnumIDList* enumItems; - HRESULT hr = currentFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &enumItems); - if (hr != S_OK) { - std::wcerr << L"Failed to get enumerator: " << hr << std::endl; - return filepath_wstring_vector; - } + IEnumIDList* enumItems; + HRESULT hr = currentFolder->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS, &enumItems); + if (hr != S_OK) { + std::wcerr << L"Failed to get enumerator: " << hr << std::endl; + return filepath_wstring_vector; + } - PITEMID_CHILD pItem; - while (enumItems->Next(1, &pItem, NULL) == S_OK && pItem) { - STRRET strRet; - WCHAR szDisplayName[MAX_PATH]; - hr = currentFolder->GetDisplayNameOf(pItem, SIGDN_FILESYSPATH, &strRet); - if (hr == S_OK) { - StrRetToBuf(&strRet, pItem, szDisplayName, MAX_PATH); - std::wcout << L"Item: " << szDisplayName << std::endl; - } - else { - std::wcerr << L"Failed to get display name: " << hr << std::endl; - } - - // Check if the item is a folder and add it to the stack for processing - SFGAOF attributes = SFGAO_FOLDER; - hr = currentFolder->GetAttributesOf(1, (PCUITEMID_CHILD*)&pItem, &attributes); - if (hr == S_OK && (attributes & SFGAO_FOLDER)) { - IShellFolder* pSubFolder; - hr = currentFolder->BindToObject(pItem, NULL, IID_IShellFolder, (void**)&pSubFolder); - if (hr == S_OK) { - folderStack.push(pSubFolder); - } - } - else - { - if (szDisplayName != nullptr) - { - std::wstring filepath_wstring = szDisplayName; - filepath_wstring_vector.push_back(filepath_wstring); - } - } - - CoTaskMemFree(pItem); + PITEMID_CHILD pItem; + while (enumItems->Next(1, &pItem, NULL) == S_OK && pItem) { + STRRET strRet; + WCHAR szDisplayName[MAX_PATH]; + hr = currentFolder->GetDisplayNameOf(pItem, SIGDN_FILESYSPATH, &strRet); + if (hr == S_OK) { + StrRetToBuf(&strRet, pItem, szDisplayName, MAX_PATH); + std::wcout << L"Item: " << szDisplayName << std::endl; + } + else { + std::wcerr << L"Failed to get display name: " << hr << std::endl; + } + + // Check if the item is a folder and add it to the stack for processing + SFGAOF attributes = SFGAO_FOLDER; + hr = currentFolder->GetAttributesOf(1, (PCUITEMID_CHILD*)&pItem, &attributes); + if (hr == S_OK && (attributes & SFGAO_FOLDER)) { + IShellFolder* pSubFolder; + hr = currentFolder->BindToObject(pItem, NULL, IID_IShellFolder, (void**)&pSubFolder); + if (hr == S_OK) { + folderStack.push(pSubFolder); } + } + else + { + if (szDisplayName != nullptr) + { + std::wstring filepath_wstring = szDisplayName; + filepath_wstring_vector.push_back(filepath_wstring); + } + } - enumItems->Release(); + CoTaskMemFree(pItem); } - return filepath_wstring_vector; + enumItems->Release(); + } + + return filepath_wstring_vector; } auto OpenFolder(HWND hwnd) { - struct RESULT { std::vector all_files_in_subfolders; std::wstring selected_path; }; - - std::vector filepath_wstring_vector = {}; - std::wstring selected_path = L""; - IFileDialog* pfd; - HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)); - if (hr != S_OK) { - std::wcerr << L"Failed to create FileOpenDialog: " << hr << std::endl; - return RESULT{ filepath_wstring_vector, selected_path }; - } + struct RESULT { std::vector all_files_in_subfolders; std::wstring selected_path; }; - DWORD dwOptions; - hr = pfd->GetOptions(&dwOptions); - if (hr != S_OK) { - std::wcerr << L"Failed to get options: " << hr << std::endl; - pfd->Release(); - return RESULT{ filepath_wstring_vector, selected_path }; - } + std::vector filepath_wstring_vector = {}; + std::wstring selected_path = L""; + IFileDialog* pfd; + HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd)); + if (hr != S_OK) { + std::wcerr << L"Failed to create FileOpenDialog: " << hr << std::endl; + return RESULT{ filepath_wstring_vector, selected_path }; + } - hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS); - if (hr != S_OK) { - std::wcerr << L"Failed to set options: " << hr << std::endl; - pfd->Release(); - return RESULT{ filepath_wstring_vector, selected_path }; - } + DWORD dwOptions; + hr = pfd->GetOptions(&dwOptions); + if (hr != S_OK) { + std::wcerr << L"Failed to get options: " << hr << std::endl; + pfd->Release(); + return RESULT{ filepath_wstring_vector, selected_path }; + } - // Show the dialog - if (pfd->Show(hwnd) == S_OK) { - IShellItem* psi; - if (pfd->GetResult(&psi) == S_OK) { - IShellFolder* pFolder; - WCHAR szPath[MAX_PATH]; - hr = psi->BindToHandler(NULL, BHID_SFObject, IID_IShellFolder, (void**)&pFolder); - if (hr == S_OK) { - PWSTR pszPath; - hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath); - if (SUCCEEDED(hr)) { - // Free the allocated memory - filepath_wstring_vector = EnumerateFolder(pFolder); - selected_path = pszPath; - CoTaskMemFree(pszPath); - } - - pFolder->Release(); - } - else { - std::wcerr << L"Failed to get shell folder: " << hr << std::endl; - } - psi->Release(); - } - else { - std::wcerr << L"Failed to get result from FileOpenDialog." << std::endl; + hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS); + if (hr != S_OK) { + std::wcerr << L"Failed to set options: " << hr << std::endl; + pfd->Release(); + return RESULT{ filepath_wstring_vector, selected_path }; + } + + // Show the dialog + if (pfd->Show(hwnd) == S_OK) { + IShellItem* psi; + if (pfd->GetResult(&psi) == S_OK) { + IShellFolder* pFolder; + WCHAR szPath[MAX_PATH]; + hr = psi->BindToHandler(NULL, BHID_SFObject, IID_IShellFolder, (void**)&pFolder); + if (hr == S_OK) { + PWSTR pszPath; + hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &pszPath); + if (SUCCEEDED(hr)) { + // Free the allocated memory + filepath_wstring_vector = EnumerateFolder(pFolder); + selected_path = pszPath; + CoTaskMemFree(pszPath); } + + pFolder->Release(); + } + else { + std::wcerr << L"Failed to get shell folder: " << hr << std::endl; + } + psi->Release(); } else { - std::wcerr << L"User canceled or an error occurred during FileOpenDialog." << std::endl; + std::wcerr << L"Failed to get result from FileOpenDialog." << std::endl; } + } + else { + std::wcerr << L"User canceled or an error occurred during FileOpenDialog." << std::endl; + } - pfd->Release(); + pfd->Release(); - return RESULT{ filepath_wstring_vector, selected_path }; + return RESULT{ filepath_wstring_vector, selected_path }; } @@ -587,38 +588,38 @@ auto OpenFolder(HWND hwnd) { void UpdateBig4PackerFilepathView(HWND hwnd, std::vector arg_filepath_vector, std::wstring selected_path) { - ListView_DeleteAllItems(hwnd); + ListView_DeleteAllItems(hwnd); - // Set redraw to false to save many instuctions. - SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); + // Set redraw to false to save many instuctions. + SendMessage(hwnd, WM_SETREDRAW, FALSE, 0); - // Iterate through the vector and add each struct's information to the ListView - LVITEM lvItem; - memset(&lvItem, 0, sizeof(LVITEM)); - lvItem.mask = LVIF_TEXT; + // Iterate through the vector and add each struct's information to the ListView + LVITEM lvItem; + memset(&lvItem, 0, sizeof(LVITEM)); + lvItem.mask = LVIF_TEXT; - std::wstring top_level_path = removeLastFolder(selected_path); + std::wstring top_level_path = removeLastFolder(selected_path); - for (size_t i = 0; i < arg_filepath_vector.size(); ++i) - { + for (size_t i = 0; i < arg_filepath_vector.size(); ++i) + { - std::wstring TOC_Compatible_Path = arg_filepath_vector[i]; - size_t pos = TOC_Compatible_Path.find(top_level_path); - - if (pos != std::wstring::npos) { - TOC_Compatible_Path = make_big_compatible_paths(pos, top_level_path, TOC_Compatible_Path); - // Add the index to the first column - lvItem.iItem = i; - lvItem.iSubItem = 0; - lvItem.pszText = LPSTR_TEXTCALLBACK; - ListView_InsertItem(hwnd, &lvItem); - ListView_SetItemText(hwnd, i, 0, const_cast(TOC_Compatible_Path.c_str())); - } - else { - std::wcout << L"Substring not found." << std::endl; - } + std::wstring TOC_Compatible_Path = arg_filepath_vector[i]; + size_t pos = TOC_Compatible_Path.find(top_level_path); + + if (pos != std::wstring::npos) { + TOC_Compatible_Path = make_big_compatible_paths(pos, top_level_path, TOC_Compatible_Path); + // Add the index to the first column + lvItem.iItem = i; + lvItem.iSubItem = 0; + lvItem.pszText = LPSTR_TEXTCALLBACK; + ListView_InsertItem(hwnd, &lvItem); + ListView_SetItemText(hwnd, i, 0, const_cast(TOC_Compatible_Path.c_str())); } - // Set redraw to true after adding items. - SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); - InvalidateRect(hwnd, NULL, TRUE); + else { + std::wcout << L"Substring not found." << std::endl; + } + } + // Set redraw to true after adding items. + SendMessage(hwnd, WM_SETREDRAW, TRUE, 0); + InvalidateRect(hwnd, NULL, TRUE); } \ No newline at end of file diff --git a/RW4ArchiveTool/Unspecified/Global.h b/RW4ArchiveTool/Unspecified/Global.h index 69243e7..ef91ad0 100644 --- a/RW4ArchiveTool/Unspecified/Global.h +++ b/RW4ArchiveTool/Unspecified/Global.h @@ -12,7 +12,7 @@ // Global variables -TCHAR szAppName[] = TEXT("RenderWare4 Archive Tool | Version 0.0.9.2"); +TCHAR szAppName[] = TEXT("RenderWare4 Archive Tool | Version 0.0.9.4"); HWND hwndMain; TCHAR szFileName[MAX_PATH] = TEXT(""); HWND hwndListView; diff --git a/RW4ArchiveTool/rw4_archive_tool.cpp b/RW4ArchiveTool/rw4_archive_tool.cpp index 7e93f13..5e7d160 100644 --- a/RW4ArchiveTool/rw4_archive_tool.cpp +++ b/RW4ArchiveTool/rw4_archive_tool.cpp @@ -1,4 +1,4 @@ - // EA Skate Archive Parser, Unpacker and Packer By GHFear. +// EA Skate Archive Parser, Unpacker and Packer By GHFear. #include "rw4_archive_tool.h" #include "Archives/Parsers/magic_parser.h" @@ -18,530 +18,527 @@ LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Entry point of the program int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { - MSG msg; - WNDCLASS wndClass = {}; - - // Register the window class. - wndClass.style = CS_HREDRAW | CS_VREDRAW; - wndClass.lpfnWndProc = WndProc; - wndClass.cbClsExtra = 0; - wndClass.cbWndExtra = 0; - wndClass.hInstance = hInstance; - wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); - wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); - wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); - wndClass.lpszMenuName = NULL; - wndClass.lpszClassName = szAppName; - - if (!RegisterClass(&wndClass)) { - MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); - return 0; - } + MSG msg; + WNDCLASS wndClass = {}; + + // Register the window class. + wndClass.style = CS_HREDRAW | CS_VREDRAW; + wndClass.lpfnWndProc = WndProc; + wndClass.cbClsExtra = 0; + wndClass.cbWndExtra = 0; + wndClass.hInstance = hInstance; + wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION); + wndClass.hCursor = LoadCursor(NULL, IDC_ARROW); + wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); + wndClass.lpszMenuName = NULL; + wndClass.lpszClassName = szAppName; + + if (!RegisterClass(&wndClass)) { + MessageBox(NULL, TEXT("This program requires Windows NT!"), szAppName, MB_ICONERROR); + return 0; + } - // Create the main window. - hwndMain = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, - CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, - NULL, NULL, hInstance, NULL); + // Create the main window. + hwndMain = CreateWindow(szAppName, szAppName, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, hInstance, NULL); - // Display the window. - ShowWindow(hwndMain, iCmdShow); - UpdateWindow(hwndMain); + // Display the window. + ShowWindow(hwndMain, iCmdShow); + UpdateWindow(hwndMain); - // Message loop. - while (GetMessage(&msg, NULL, 0, 0)) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } + // Message loop. + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } - return msg.wParam; + return msg.wParam; } // Window procedure. LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { - switch (message) { - case WM_CREATE: + switch (message) { + case WM_CREATE: + { + // Initialize the common controls (If we need them at some point) + INITCOMMONCONTROLSEX icex = {}; + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + // Create Menu + HMENU hMenu = CreateMenu(); + HMENU hSubMenu = CreateMenu(); + AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, TEXT("Open")); + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, TEXT("File")); + + HMENU hSubMenu2 = CreateMenu(); + AppendMenu(hSubMenu2, MF_STRING, ID_FIND_IN_FILES, TEXT("Find In Files")); + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu2, TEXT("Find")); + + HMENU hSubMenuExperimental = CreateMenu(); + HMENU hSubMenuPackers = CreateMenu(); + AppendMenu(hSubMenuExperimental, MF_STRING, ID_BIG4_PACKER_MENU, TEXT("Build BIG4/BIGF")); + AppendMenu(hSubMenuExperimental, MF_STRING, ID_BIGEB_PACKER_MENU, TEXT("Build Big(EB)")); + AppendMenu(hSubMenuExperimental, MF_STRING, ID_SF_PACKER_MENU, TEXT("Build SF")); + AppendMenu(hSubMenuExperimental, MF_STRING, ID_ARENA_PACKER_MENU, TEXT("Build Arena")); + AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenuExperimental, TEXT("Experimental")); + + // Assign the menu to the window + SetMenu(hwnd, hMenu); + + // Create a ListView to display file information + hwndListView = CreateWindow(WC_LISTVIEW, NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | WS_CLIPSIBLINGS, + 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW, GetModuleHandle(NULL), NULL); + + // Set the extended style + ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); + + // Set up columns in the ListView + LVCOLUMN lvc1 = {}; + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 150; // Width of the column + lvc1.pszText = (LPWSTR)L"Archive"; + lvc1.iSubItem = 0; + ListView_InsertColumn(hwndListView, 0, &lvc1); + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 75; + lvc1.pszText = (LPWSTR)L"Size"; + lvc1.iSubItem = 1; + ListView_InsertColumn(hwndListView, 1, &lvc1); + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 50; + lvc1.pszText = (LPWSTR)L"Path"; + lvc1.iSubItem = 2; + ListView_InsertColumn(hwndListView, 2, &lvc1); + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 50; + lvc1.pszText = (LPWSTR)L"Format"; + lvc1.iSubItem = 3; + ListView_InsertColumn(hwndListView, 3, &lvc1); + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 50; + lvc1.pszText = (LPWSTR)L"Ver."; + lvc1.iSubItem = 4; + ListView_InsertColumn(hwndListView, 4, &lvc1); + + lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc1.cx = 50; + lvc1.pszText = (LPWSTR)L"Collections"; + lvc1.iSubItem = 5; + ListView_InsertColumn(hwndListView, 5, &lvc1); + + // Enable drag-and-drop + DragAcceptFiles(hwndListView, TRUE); + + // Subclass the first ListView control + SubclassListView(hwndListView); + + // Create a ListView to display file information + hwndListView2 = CreateWindow(WC_LISTVIEW, NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_CLIPSIBLINGS, + 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW2, GetModuleHandle(NULL), NULL); + + // Set the extended style + ListView_SetExtendedListViewStyle(hwndListView2, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); + + // Set up columns in the ListView + LVCOLUMN lvc2 = {}; + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 50; // Width of the column + lvc2.pszText = (LPWSTR)L"Index"; + lvc2.iSubItem = 0; + ListView_InsertColumn(hwndListView2, 0, &lvc2); + + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 650; + lvc2.pszText = (LPWSTR)L"Filename"; + lvc2.iSubItem = 1; + ListView_InsertColumn(hwndListView2, 1, &lvc2); + + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 75; + lvc2.pszText = (LPWSTR)L"Size"; + lvc2.iSubItem = 2; + ListView_InsertColumn(hwndListView2, 2, &lvc2); + + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 75; + lvc2.pszText = (LPWSTR)L"Offset"; + lvc2.iSubItem = 3; + ListView_InsertColumn(hwndListView2, 3, &lvc2); + + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 100; + lvc2.pszText = (LPWSTR)L"ZType"; + lvc2.iSubItem = 4; + ListView_InsertColumn(hwndListView2, 4, &lvc2); + + lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc2.cx = 100; + lvc2.pszText = (LPWSTR)L"TOC Offset"; + lvc2.iSubItem = 5; + ListView_InsertColumn(hwndListView2, 5, &lvc2); + + // Subclass the second ListView control + SubclassListView(hwndListView2); + + // Create a ListView to display file information + hwndListView3 = CreateWindow(WC_LISTVIEW, NULL, + WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_CLIPSIBLINGS, + 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW3, GetModuleHandle(NULL), NULL); + + // Set the extended style + ListView_SetExtendedListViewStyle(hwndListView3, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); + + // Set up columns in the ListView + LVCOLUMN lvc3 = {}; + + lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc3.cx = 125; // Width of the column + lvc3.pszText = (LPWSTR)L"These lists"; + lvc3.iSubItem = 0; + ListView_InsertColumn(hwndListView3, 0, &lvc3); + + lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc3.cx = 125; + lvc3.pszText = (LPWSTR)L"will display"; + lvc3.iSubItem = 1; + ListView_InsertColumn(hwndListView3, 1, &lvc3); + + lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc3.cx = 150; + lvc3.pszText = (LPWSTR)L"information about"; + lvc3.iSubItem = 2; + ListView_InsertColumn(hwndListView3, 2, &lvc3); + + lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc3.cx = 150; + lvc3.pszText = (LPWSTR)L"whatever you're"; + lvc3.iSubItem = 3; + ListView_InsertColumn(hwndListView3, 3, &lvc3); + + lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; + lvc3.cx = 150; + lvc3.pszText = (LPWSTR)L"highlighting."; + lvc3.iSubItem = 4; + ListView_InsertColumn(hwndListView3, 4, &lvc3); + + // Create context menu and set the member menu items + hContextMenuArchives = CreatePopupMenu(); + AppendMenu(hContextMenuArchives, MF_STRING, ID_ARCHIVEMENU_ITEM1, L"Unpack"); + AppendMenu(hContextMenuArchives, MF_STRING, ID_ARCHIVEMENU_ITEM2, L"Open Directory"); + + // Create context menu and set the member menu items + hContextMenuFiles = CreatePopupMenu(); + AppendMenu(hContextMenuFiles, MF_STRING, ID_FILEMENU_ITEM1, L"Unpack File"); + + break; + } + case WM_SIZE: + { + // Set the size for the listviews + int windowWidth = LOWORD(lParam); + int windowHeight = HIWORD(lParam); + + // left side + int left_listview_width = ((windowWidth) / 4); + int left_listview_height = std::round((2.0 * windowHeight) / 3.0); + + // Right side top + int right_top_listview_width = windowWidth - left_listview_width; + int right_top_listview_height = std::round((2.0 * windowHeight) / 3.0); + + // Right side bottom + int right_bottom_listview_width = windowWidth - left_listview_width; + int right_bottom_listview_height = windowHeight - right_top_listview_height; + + // Padding + int middle_padding = 6; + int right_divider_padding = 1; + int outer_border_padding = 5; + + MoveWindow(hwndListView, 5, 0, left_listview_width - middle_padding, windowHeight - outer_border_padding, TRUE); + MoveWindow(hwndListView2, left_listview_width, 0, right_top_listview_width - outer_border_padding, right_top_listview_height - right_divider_padding, TRUE); + MoveWindow(hwndListView3, left_listview_width, right_top_listview_height, right_bottom_listview_width - outer_border_padding, right_bottom_listview_height - outer_border_padding, TRUE); + + SendMessage(Big4PackerWindow::hwnd_CreateBigPacker, WM_SIZE, wParam, lParam); + + break; + } + case WM_NOTIFY: + { + LPNMHDR pnmh = (LPNMHDR)lParam; + if (pnmh->idFrom == ID_LIST_VIEW) { - // Initialize the common controls (If we need them at some point) - INITCOMMONCONTROLSEX icex = {}; - icex.dwSize = sizeof(INITCOMMONCONTROLSEX); - icex.dwICC = ICC_LISTVIEW_CLASSES; - InitCommonControlsEx(&icex); - - // Create Menu - HMENU hMenu = CreateMenu(); - HMENU hSubMenu = CreateMenu(); - AppendMenu(hSubMenu, MF_STRING, ID_FILE_OPEN, TEXT("Open")); - AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu, TEXT("File")); - - HMENU hSubMenu2 = CreateMenu(); - AppendMenu(hSubMenu2, MF_STRING, ID_FIND_IN_FILES, TEXT("Find In Files")); - AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenu2, TEXT("Find")); - - HMENU hSubMenuExperimental = CreateMenu(); - HMENU hSubMenuPackers = CreateMenu(); - AppendMenu(hSubMenuExperimental, MF_STRING, ID_BIG4_PACKER_MENU, TEXT("Build BIG4/BIGF")); - AppendMenu(hSubMenuExperimental, MF_STRING, ID_BIGEB_PACKER_MENU, TEXT("Build Big(EB)")); - AppendMenu(hSubMenuExperimental, MF_STRING, ID_SF_PACKER_MENU, TEXT("Build SF")); - AppendMenu(hSubMenuExperimental, MF_STRING, ID_ARENA_PACKER_MENU, TEXT("Build Arena")); - AppendMenu(hMenu, MF_STRING | MF_POPUP, (UINT)hSubMenuExperimental, TEXT("Experimental")); - - // Assign the menu to the window - SetMenu(hwnd, hMenu); - - // Create a ListView to display file information - hwndListView = CreateWindow(WC_LISTVIEW, NULL, - WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | WS_CLIPSIBLINGS, - 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW, GetModuleHandle(NULL), NULL); - - // Set the extended style - ListView_SetExtendedListViewStyle(hwndListView, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); - - // Set up columns in the ListView - LVCOLUMN lvc1 = {}; - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 150; // Width of the column - lvc1.pszText = (LPWSTR)L"Archive"; - lvc1.iSubItem = 0; - ListView_InsertColumn(hwndListView, 0, &lvc1); - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 75; // Width of the column - lvc1.pszText = (LPWSTR)L"Size"; - lvc1.iSubItem = 1; - ListView_InsertColumn(hwndListView, 1, &lvc1); - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 50; // Width of the column - lvc1.pszText = (LPWSTR)L"Path"; - lvc1.iSubItem = 2; - ListView_InsertColumn(hwndListView, 2, &lvc1); - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 50; // Width of the column - lvc1.pszText = (LPWSTR)L"Format"; - lvc1.iSubItem = 3; - ListView_InsertColumn(hwndListView, 3, &lvc1); - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 50; // Width of the column - lvc1.pszText = (LPWSTR)L"Ver."; - lvc1.iSubItem = 4; - ListView_InsertColumn(hwndListView, 4, &lvc1); - - lvc1.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc1.cx = 50; // Width of the column - lvc1.pszText = (LPWSTR)L"Collections"; - lvc1.iSubItem = 5; - ListView_InsertColumn(hwndListView, 5, &lvc1); - - // Enable drag-and-drop - DragAcceptFiles(hwndListView, TRUE); - - // Subclass the first ListView control - SubclassListView(hwndListView); - - // Create a ListView to display file information - hwndListView2 = CreateWindow(WC_LISTVIEW, NULL, - WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_CLIPSIBLINGS, - 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW2, GetModuleHandle(NULL), NULL); - - // Set the extended style - ListView_SetExtendedListViewStyle(hwndListView2, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); - - // Set up columns in the ListView - LVCOLUMN lvc2 = {}; - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 50; // Width of the column - lvc2.pszText = (LPWSTR)L"Index"; - lvc2.iSubItem = 0; - ListView_InsertColumn(hwndListView2, 0, &lvc2); - - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 650; - lvc2.pszText = (LPWSTR)L"Filename"; - lvc2.iSubItem = 1; - ListView_InsertColumn(hwndListView2, 1, &lvc2); - - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 75; - lvc2.pszText = (LPWSTR)L"Size"; - lvc2.iSubItem = 2; - ListView_InsertColumn(hwndListView2, 2, &lvc2); - - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 75; - lvc2.pszText = (LPWSTR)L"Offset"; - lvc2.iSubItem = 3; - ListView_InsertColumn(hwndListView2, 3, &lvc2); - - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 100; - lvc2.pszText = (LPWSTR)L"ZType"; - lvc2.iSubItem = 4; - ListView_InsertColumn(hwndListView2, 4, &lvc2); - - lvc2.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc2.cx = 100; - lvc2.pszText = (LPWSTR)L"TOC Offset"; - lvc2.iSubItem = 5; - ListView_InsertColumn(hwndListView2, 5, &lvc2); - - // Subclass the second ListView control - SubclassListView(hwndListView2); - - // Create a ListView to display file information - hwndListView3 = CreateWindow(WC_LISTVIEW, NULL, - WS_CHILD | WS_VISIBLE | WS_BORDER | LVS_REPORT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | WS_CLIPSIBLINGS, - 0, 0, 0, 0, hwnd, (HMENU)ID_LIST_VIEW3, GetModuleHandle(NULL), NULL); - - // Set the extended style - ListView_SetExtendedListViewStyle(hwndListView3, LVS_EX_DOUBLEBUFFER | LVS_EX_FULLROWSELECT); - - // Set up columns in the ListView - LVCOLUMN lvc3 = {}; - - lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc3.cx = 125; // Width of the column - lvc3.pszText = (LPWSTR)L"These lists"; - lvc3.iSubItem = 0; - ListView_InsertColumn(hwndListView3, 0, &lvc3); - - lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc3.cx = 125; // Width of the column - lvc3.pszText = (LPWSTR)L"will display"; - lvc3.iSubItem = 1; - ListView_InsertColumn(hwndListView3, 1, &lvc3); - - lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc3.cx = 150; // Width of the column - lvc3.pszText = (LPWSTR)L"information about"; - lvc3.iSubItem = 2; - ListView_InsertColumn(hwndListView3, 2, &lvc3); - - lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc3.cx = 150; // Width of the column - lvc3.pszText = (LPWSTR)L"whatever you're"; - lvc3.iSubItem = 3; - ListView_InsertColumn(hwndListView3, 3, &lvc3); - - lvc3.mask = LVCF_TEXT | LVCF_WIDTH | LVCF_SUBITEM; - lvc3.cx = 150; // Width of the column - lvc3.pszText = (LPWSTR)L"highlighting."; - lvc3.iSubItem = 4; - ListView_InsertColumn(hwndListView3, 4, &lvc3); - - // Create context menu and set the member menu items - hContextMenuArchives = CreatePopupMenu(); - AppendMenu(hContextMenuArchives, MF_STRING, ID_ARCHIVEMENU_ITEM1, L"Unpack"); - AppendMenu(hContextMenuArchives, MF_STRING, ID_ARCHIVEMENU_ITEM2, L"Open Directory"); - - // Create context menu and set the member menu items - hContextMenuFiles = CreatePopupMenu(); - AppendMenu(hContextMenuFiles, MF_STRING, ID_FILEMENU_ITEM1, L"Unpack File"); + switch (pnmh->code) + { + case NM_RCLICK: + { + // Handle right-click or left-click + LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam; + int selectedItem = lpnmitem->iItem; + if (selectedItem != -1) + { + // Get the cursor position + POINT cursor; + GetCursorPos(&cursor); + // Track the context menu + TrackPopupMenu(hContextMenuArchives, TPM_LEFTALIGN | TPM_TOPALIGN, cursor.x, cursor.y, 0, hwnd, NULL); + } break; - } - case WM_SIZE: + } + case NM_DBLCLK: + // Handle double-click + LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)lParam; + + // Get the selected item text + int selectedItemIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED); + if (selectedItemIndex != -1) { + wchar_t Filename[512] = {}; + wchar_t FileDirectory[512] = {}; + ListView_GetItemText(hwndListView, selectedItemIndex, 0, Filename, sizeof(Filename) / sizeof(Filename[0])); + ListView_GetItemText(hwndListView, selectedItemIndex, 2, FileDirectory, sizeof(FileDirectory) / sizeof(FileDirectory[0])); + + // Create file path wstring and update window title. + std::wstring FullPath = FileDirectory; + FullPath += Filename; + GlobalLoadedArchive = FullPath; + UpdateWindowTitle(FullPath); + + bool successful_parse = false; + // Parse Archive + switch (magic::magic_parser(FullPath.c_str())) + { + case BIG_EB: // Parse Big (EB) + { + auto parse_result = big_eb::parse_big_eb_archive(FullPath.c_str(), false, -1); + parsed_archive = parse_result.parsed_info; + successful_parse = parse_result.success; + break; + } + case BIG4: // Parse Big (BIG4 / BIGF) + { + auto parse_result = big4::parse_big4_archive(FullPath.c_str(), false, -1); + parsed_archive = parse_result.parsed_info; + successful_parse = parse_result.success; + break; + } + case SFIL: // Parse SFIL + { + auto parse_result = sf::parse_sf_archive(FullPath.c_str(), false, -1); + parsed_archive = parse_result.parsed_info; + successful_parse = parse_result.success; + break; + } + case ARENA: // Parse Arena + { + auto parse_result = arena::parse_arena_filepackage(FullPath.c_str(), false, -1); + parsed_archive = parse_result.parsed_info; + successful_parse = parse_result.success; + break; + } + case UNKNOWNARCHIVE: + break; + default: + break; + } + + if (successful_parse) // Update the fileview if we successfully parsed the files. + { + UpdateFileView(hwndListView2, parsed_archive); // Update our listview on double click. + } + else + { + MessageBox(hwnd, L"Error parsing archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + } + + break; + } + break; + } + } + else if (pnmh->idFrom == ID_LIST_VIEW2) { - // Set the size for the listviews - int windowWidth = LOWORD(lParam); - int windowHeight = HIWORD(lParam); - - // left side - int left_listview_width = ((windowWidth) / 4); - int left_listview_height = std::round((2.0 * windowHeight) / 3.0); + switch (pnmh->code) + { + case NM_RCLICK: + { + // Handle right-click or left-click + LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam; + int selectedItem = lpnmitem->iItem; + if (selectedItem != -1) + { + // Get the cursor position + POINT cursor; + GetCursorPos(&cursor); - // Right side top - int right_top_listview_width = windowWidth - left_listview_width; - int right_top_listview_height = std::round((2.0 * windowHeight) / 3.0); + // Track the context menu + TrackPopupMenu(hContextMenuFiles, TPM_LEFTALIGN | TPM_TOPALIGN, cursor.x, cursor.y, 0, hwnd, NULL); + } + break; + } + case NM_DBLCLK: + break; + } + } + break; + } + case WM_COMMAND: + switch (LOWORD(wParam)) { + case ID_FIND_IN_FILES: + FindFileWindow::CreateFindInFilesWindow(parsed_archive); + break; + case ID_BIG4_PACKER_MENU: + Big4PackerWindow::CreateBig4PackerWindow(hwnd); + break; + case ID_BIGEB_PACKER_MENU: + MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); + break; + case ID_SF_PACKER_MENU: + MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); + break; + case ID_ARENA_PACKER_MENU: + MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); + break; + case ID_FILE_OPEN: + OpenFileAndLoadBuffer(hwnd); + break; + case ID_ARCHIVEMENU_ITEM1: + { + // Get paths from all selected items + std::vector selectedPaths = GetSelectedPaths(hwndListView); - // Right side bottom - int right_bottom_listview_width = windowWidth - left_listview_width; - int right_bottom_listview_height = windowHeight - right_top_listview_height; + // Now, selectedPaths vector contains the paths of all selected items in the ListView + for (const auto& path : selectedPaths) { - // Padding - int middle_padding = 6; - int right_divider_padding = 1; - int outer_border_padding = 5; + bool successful_parse = false; - MoveWindow(hwndListView, 5, 0, left_listview_width - middle_padding, windowHeight - outer_border_padding, TRUE); - MoveWindow(hwndListView2, left_listview_width, 0, right_top_listview_width - outer_border_padding, right_top_listview_height - right_divider_padding, TRUE); - MoveWindow(hwndListView3, left_listview_width, right_top_listview_height, right_bottom_listview_width - outer_border_padding, right_bottom_listview_height - outer_border_padding, TRUE); + // Unpack type selector. + switch (magic::magic_parser(path.c_str())) + { + case BIG_EB: // Unpack Big (EB) + { + auto unpack_result = big_eb::parse_big_eb_archive(path.c_str(), true, -1); + successful_parse = unpack_result.success; + break; + } + case BIG4: // Unpack Big (BIG4 / BIGF) + { + auto unpack_result = big4::parse_big4_archive(path.c_str(), true, -1); + successful_parse = unpack_result.success; + break; + } + case SFIL: // Unpack SFIL + { + auto unpack_result = sf::parse_sf_archive(path.c_str(), true, -1); + successful_parse = unpack_result.success; + break; + } + case ARENA: // Unpack Arena + { + auto unpack_result = arena::parse_arena_filepackage(path.c_str(), true, -1); + successful_parse = unpack_result.success; + break; + } + case UNKNOWNARCHIVE: + MessageBox(hwnd, L"Archive is of unknown type!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + break; + default: + break; + } - SendMessage(Big4PackerWindow::hwnd_CreateBigPacker, WM_SIZE, wParam, lParam); + if (!successful_parse) // If error + { + MessageBox(hwnd, L"Error unpacking archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + } + } - break; + // Show if everything went well. + MessageBox(hwnd, L"Unpacking routine complete!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + break; + } + case ID_ARCHIVEMENU_ITEM2: + { + // Get the selected item text + int selectedItemIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED); + if (selectedItemIndex != -1) { + wchar_t FileDirectory[256] = {}; + ListView_GetItemText(hwndListView, selectedItemIndex, 2, FileDirectory, sizeof(FileDirectory) / sizeof(FileDirectory[0])); + ShellExecute(NULL, L"open", L"explorer.exe", FileDirectory, NULL, SW_SHOWNORMAL); + } + break; } - case WM_NOTIFY: + case ID_FILEMENU_ITEM1: { - LPNMHDR pnmh = (LPNMHDR)lParam; - if (pnmh->idFrom == ID_LIST_VIEW) + // Get the selected item text + int selectedItemIndex = ListView_GetNextItem(hwndListView2, -1, LVNI_SELECTED); + if (selectedItemIndex != -1) { + bool successful_parse = false; + + // Unpack type selector. + switch (magic::magic_parser(GlobalLoadedArchive.c_str())) { - switch (pnmh->code) - { - case NM_RCLICK: - { - // Handle right-click or left-click - LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam; - int selectedItem = lpnmitem->iItem; - if (selectedItem != -1) - { - // Get the cursor position - POINT cursor; - GetCursorPos(&cursor); - - // Track the context menu - TrackPopupMenu(hContextMenuArchives, TPM_LEFTALIGN | TPM_TOPALIGN, cursor.x, cursor.y, 0, hwnd, NULL); - } - break; - } - case NM_DBLCLK: - // Handle double-click - LPNMLISTVIEW pnmlv = (LPNMLISTVIEW)lParam; - - // Get the selected item text - int selectedItemIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED); - if (selectedItemIndex != -1) { - wchar_t Filename[512] = {}; - wchar_t FileDirectory[512] = {}; - ListView_GetItemText(hwndListView, selectedItemIndex, 0, Filename, sizeof(Filename) / sizeof(Filename[0])); - ListView_GetItemText(hwndListView, selectedItemIndex, 2, FileDirectory, sizeof(FileDirectory) / sizeof(FileDirectory[0])); - - // Create file path wstring and update window title. - std::wstring FullPath = FileDirectory; - FullPath += Filename; - GlobalLoadedArchive = FullPath; - UpdateWindowTitle(FullPath); - - bool successful_parse = false; - // Parse Archive - switch (magic::magic_parser(FullPath.c_str())) - { - case BIG_EB: - { - auto parse_result = big_eb::parse_big_eb_archive(FullPath.c_str(), false, -1); - parsed_archive = parse_result.parsed_info; - successful_parse = parse_result.success; - break; - } - case BIG4: - { - auto parse_result = big4::parse_big4_archive(FullPath.c_str(), false, -1); - parsed_archive = parse_result.parsed_info; - successful_parse = parse_result.success; - break; - } - case SFIL: - { - auto parse_result = sf::parse_sf_archive(FullPath.c_str(), false, -1); - parsed_archive = parse_result.parsed_info; - successful_parse = parse_result.success; - break; - } - case ARENA: - { - auto parse_result = arena::parse_arena_filepackage(FullPath.c_str(), false, -1); - parsed_archive = parse_result.parsed_info; - successful_parse = parse_result.success; - break; - } - case UNKNOWNARCHIVE: - break; - default: - break; - } - - if (successful_parse) - { - UpdateFileView(hwndListView2, parsed_archive); // Update our listview on double click. - } - else - { - MessageBox(hwnd, L"Error parsing archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - } - - break; - } - break; - } - } - else if (pnmh->idFrom == ID_LIST_VIEW2) + case BIG_EB: // Unpack single file from Big (EB) { - switch (pnmh->code) - { - case NM_RCLICK: - { - // Handle right-click or left-click - LPNMITEMACTIVATE lpnmitem = (LPNMITEMACTIVATE)lParam; - int selectedItem = lpnmitem->iItem; - if (selectedItem != -1) - { - // Get the cursor position - POINT cursor; - GetCursorPos(&cursor); - - // Track the context menu - TrackPopupMenu(hContextMenuFiles, TPM_LEFTALIGN | TPM_TOPALIGN, cursor.x, cursor.y, 0, hwnd, NULL); - } - break; - } - case NM_DBLCLK: - break; - } + auto unpack_result = big_eb::parse_big_eb_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); + successful_parse = unpack_result.success; + break; } - break; - } - case WM_COMMAND: - switch (LOWORD(wParam)) { - case ID_FIND_IN_FILES: - FindFileWindow::CreateFindInFilesWindow(parsed_archive); - break; - case ID_BIG4_PACKER_MENU: - Big4PackerWindow::CreateBig4PackerWindow(hwnd); - break; - case ID_BIGEB_PACKER_MENU: - MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); - break; - case ID_SF_PACKER_MENU: - MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); - break; - case ID_ARENA_PACKER_MENU: - MessageBox(hwnd, L"This feature hasn't been implemented just yet ;)", L"Packer Prompt", MB_OK | MB_ICONINFORMATION); - break; - case ID_FILE_OPEN: - OpenFileAndLoadBuffer(hwnd); - break; - case ID_ARCHIVEMENU_ITEM1: + case BIG4: // Unpack single file from Big (BIG4 / BIGF) { - // Get paths from all selected items - std::vector selectedPaths = GetSelectedPaths(hwndListView); - - // Now, selectedPaths vector contains the paths of all selected items in the ListView - for (const auto& path : selectedPaths) { - - bool successful_parse = false; - - // Unpack type selector. - switch (magic::magic_parser(path.c_str())) - { - case BIG_EB: - { - auto unpack_result = big_eb::parse_big_eb_archive(path.c_str(), true, -1); - successful_parse = unpack_result.success; - break; - } - case BIG4: - { - auto unpack_result = big4::parse_big4_archive(path.c_str(), true, -1); - successful_parse = unpack_result.success; - break; - } - case SFIL: - { - auto unpack_result = sf::parse_sf_archive(path.c_str(), true, -1); - successful_parse = unpack_result.success; - break; - } - case ARENA: - { - auto unpack_result = arena::parse_arena_filepackage(path.c_str(), true, -1); - successful_parse = unpack_result.success; - break; - } - case UNKNOWNARCHIVE: - MessageBox(hwnd, L"Archive is of unknown type!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - break; - default: - break; - } - - if (!successful_parse) - { - MessageBox(hwnd, L"Error unpacking archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - } - - } - - MessageBox(hwnd, L"Unpacking routine complete!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - - break; + auto unpack_result = big4::parse_big4_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); + successful_parse = unpack_result.success; + break; } - case ID_ARCHIVEMENU_ITEM2: + case SFIL: // Unpack single file from SFIL { - // Get the selected item text - int selectedItemIndex = ListView_GetNextItem(hwndListView, -1, LVNI_SELECTED); - if (selectedItemIndex != -1) { - wchar_t FileDirectory[256] = {}; - ListView_GetItemText(hwndListView, selectedItemIndex, 2, FileDirectory, sizeof(FileDirectory) / sizeof(FileDirectory[0])); - - ShellExecute(NULL, L"open", L"explorer.exe", FileDirectory, NULL, SW_SHOWNORMAL); - } - break; + auto unpack_result = sf::parse_sf_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); + successful_parse = unpack_result.success; + break; } - case ID_FILEMENU_ITEM1: + case ARENA: // Unpack single file from Arena { - // Get the selected item text - int selectedItemIndex = ListView_GetNextItem(hwndListView2, -1, LVNI_SELECTED); - if (selectedItemIndex != -1) { - bool successful_parse = false; - - // Unpack type selector. - switch (magic::magic_parser(GlobalLoadedArchive.c_str())) - { - case BIG_EB: - { - auto unpack_result = big_eb::parse_big_eb_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); - successful_parse = unpack_result.success; - break; - } - case BIG4: - { - auto unpack_result = big4::parse_big4_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); - successful_parse = unpack_result.success; - break; - } - case SFIL: - { - auto unpack_result = sf::parse_sf_archive(GlobalLoadedArchive.c_str(), true, selectedItemIndex); - successful_parse = unpack_result.success; - break; - } - case ARENA: - { - auto unpack_result = arena::parse_arena_filepackage(GlobalLoadedArchive.c_str(), true, selectedItemIndex); - successful_parse = unpack_result.success; - break; - } - case UNKNOWNARCHIVE: - MessageBox(hwnd, L"Archive is of unknown type!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - break; - default: - break; - } - - if (!successful_parse) - { - MessageBox(hwnd, L"Error unpacking archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - } - else - { - MessageBox(hwnd, L"Unpacking routine complete!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); - } - } - break; + auto unpack_result = arena::parse_arena_filepackage(GlobalLoadedArchive.c_str(), true, selectedItemIndex); + successful_parse = unpack_result.success; + break; } + case UNKNOWNARCHIVE: + MessageBox(hwnd, L"Archive is of unknown type!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + break; + default: + break; } - break; - case WM_DESTROY: - DragAcceptFiles(hwndListView, FALSE); - PostQuitMessage(0); - break; - default: - return DefWindowProc(hwnd, message, wParam, lParam); + if (!successful_parse) // If error + { + MessageBox(hwnd, L"Error unpacking archive!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + } + else // If success + { + MessageBox(hwnd, L"Unpacking routine complete!", L"Unpacker Prompt", MB_OK | MB_ICONINFORMATION); + } + } + break; }; // Switch end bracket } + break; + case WM_DESTROY: + DragAcceptFiles(hwndListView, FALSE); + PostQuitMessage(0); + break; - return 0; + default: + return DefWindowProc(hwnd, message, wParam, lParam); + } + + return 0; } \ No newline at end of file