mirror of
https://github.com/wiidev/usbloadergx.git
synced 2024-11-05 19:15:07 +01:00
a4f3d16a56
*Added support for older Hermes IOS too (rev 2 and rev 3) *Changed Alternative Dol Browser to get the filelist (fst) with functions from libwbfs (like banner sounds) instead of from an emulated disk. This should fix all the weird crashes with it.
563 lines
13 KiB
C
563 lines
13 KiB
C
/****************************************************************************
|
|
* USB Loader GX Team
|
|
* openingbnr
|
|
*
|
|
* Extract opening.bnr/banner.bin/sound.bin/icon.bin
|
|
*
|
|
* Copyright 2008 Magicus <magicus@gmail.com>
|
|
* Licensed under the terms of the GNU GPL, version 2
|
|
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
|
|
* Version 1.0 Initial release
|
|
***************************************************************************/
|
|
|
|
#define _GNU_SOURCE
|
|
|
|
#include <ogcsys.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "libfat/fat.h"
|
|
|
|
#include "MD5.h"
|
|
#include "openingbnr.h"
|
|
#include "FileOperations/fileops.h"
|
|
|
|
u16 be16(const u8 *p)
|
|
{
|
|
return (p[0] << 8) | p[1];
|
|
}
|
|
|
|
u32 be32(const u8 *p)
|
|
{
|
|
return (p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3];
|
|
}
|
|
|
|
u64 be64(const u8 *p)
|
|
{
|
|
return ((u64) be32(p) << 32) | be32(p + 4);
|
|
}
|
|
|
|
u64 be34(const u8 *p)
|
|
{
|
|
return 4 * (u64) be32(p);
|
|
}
|
|
|
|
void wbe16(u8 *p, u16 x)
|
|
{
|
|
p[0] = x >> 8;
|
|
p[1] = x;
|
|
}
|
|
|
|
void wbe32(u8 *p, u32 x)
|
|
{
|
|
wbe16(p, x >> 16);
|
|
wbe16(p + 2, x);
|
|
}
|
|
|
|
void wbe64(u8 *p, u64 x)
|
|
{
|
|
wbe32(p, x >> 32);
|
|
wbe32(p + 4, x);
|
|
}
|
|
|
|
void md5(u8 *data, u32 len, u8 *hash)
|
|
{
|
|
MD5(hash, data, len);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
u8 zeroes[0x40];
|
|
u32 imet; // "IMET"
|
|
u8 zero_six_zero_three[8]; // fixed, unknown purpose
|
|
u32 sizes[3];
|
|
u32 flag1;
|
|
u16 name_jp[0x2a]; // might be empty
|
|
u16 name_en[0x2a];
|
|
u16 name_de[0x2a];
|
|
u16 name_fr[0x2a];
|
|
u16 name_es[0x2a];
|
|
u16 name_it[0x2a];
|
|
u16 name_nl[0x2a];
|
|
u8 zeroes_2[0x348];
|
|
u8 crypto[0x10];
|
|
} imet_data_t;
|
|
|
|
typedef struct
|
|
{
|
|
u32 imd5_tag; // 0x494D4435 "IMD5";
|
|
u32 size; // size of the rest of part B, starting from next field.
|
|
u8 zeroes[8];
|
|
u8 md5[16];
|
|
u32 payload_tag; // 0x4C5A3737 "LZ77" if this is lz77
|
|
u32 payload_data;
|
|
} imd5_header_t;
|
|
|
|
typedef struct
|
|
{
|
|
u16 type;
|
|
u16 name_offset;
|
|
u32 data_offset; // == absolut offset från U.8- headerns början
|
|
u32 size; // last included file num for directories
|
|
} U8_node;
|
|
|
|
typedef struct
|
|
{
|
|
u32 tag; // 0x55AA382D "U.8-"
|
|
u32 rootnode_offset; // offset to root_node, always 0x20.
|
|
u32 header_size; // size of header from root_node to end of string table.
|
|
u32 data_offset; // offset to data -- this is rootnode_offset + header_size, aligned to 0x40.
|
|
u8 zeroes[16];
|
|
} U8_archive_header;
|
|
|
|
static int write_file(void* data, size_t size, char* name)
|
|
{
|
|
size_t written = 0;
|
|
FILE *out;
|
|
out = fopen(name, "wb");
|
|
if (out)
|
|
{
|
|
written = fwrite(data, 1, size, out);
|
|
fclose(out);
|
|
}
|
|
return (written == size) ? 1 : -1;
|
|
}
|
|
|
|
u8* decompress_lz77(u8 *data, size_t data_size, size_t* decompressed_size)
|
|
{
|
|
u8 *data_end;
|
|
u8 *decompressed_data;
|
|
size_t unpacked_size;
|
|
u8 *in_ptr;
|
|
u8 *out_ptr;
|
|
u8 *out_end;
|
|
|
|
in_ptr = data;
|
|
data_end = data + data_size;
|
|
|
|
// Assume this for now and grow when needed
|
|
unpacked_size = data_size;
|
|
|
|
decompressed_data = malloc(unpacked_size);
|
|
out_end = decompressed_data + unpacked_size;
|
|
|
|
out_ptr = decompressed_data;
|
|
|
|
while (in_ptr < data_end)
|
|
{
|
|
int bit;
|
|
u8 bitmask = *in_ptr;
|
|
|
|
in_ptr++;
|
|
for (bit = 0x80; bit != 0; bit >>= 1)
|
|
{
|
|
if (bitmask & bit)
|
|
{
|
|
// Next section is compressed
|
|
u8 rep_length;
|
|
u16 rep_offset;
|
|
|
|
rep_length = (*in_ptr >> 4) + 3;
|
|
rep_offset = *in_ptr & 0x0f;
|
|
in_ptr++;
|
|
rep_offset = *in_ptr | (rep_offset << 8);
|
|
in_ptr++;
|
|
if (out_ptr - decompressed_data < rep_offset)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
for (; rep_length > 0; rep_length--)
|
|
{
|
|
*out_ptr = out_ptr[-rep_offset - 1];
|
|
out_ptr++;
|
|
if (out_ptr >= out_end)
|
|
{
|
|
// Need to grow buffer
|
|
decompressed_data = realloc(decompressed_data, unpacked_size * 2);
|
|
out_ptr = decompressed_data + unpacked_size;
|
|
unpacked_size *= 2;
|
|
out_end = decompressed_data + unpacked_size;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Just copy byte
|
|
*out_ptr = *in_ptr;
|
|
out_ptr++;
|
|
if (out_ptr >= out_end)
|
|
{
|
|
// Need to grow buffer
|
|
decompressed_data = realloc(decompressed_data, unpacked_size * 2);
|
|
out_ptr = decompressed_data + unpacked_size;
|
|
unpacked_size *= 2;
|
|
out_end = decompressed_data + unpacked_size;
|
|
}
|
|
in_ptr++;
|
|
}
|
|
}
|
|
}
|
|
|
|
*decompressed_size = (out_ptr - decompressed_data);
|
|
return decompressed_data;
|
|
}
|
|
|
|
static int write_imd5_lz77(u8* data, size_t size, char* outname)
|
|
{
|
|
imd5_header_t* header = (imd5_header_t*) data;
|
|
u32 tag;
|
|
u32 size_in_imd5;
|
|
u8 md5_calc[16];
|
|
u8 *decompressed_data;
|
|
size_t decompressed_size;
|
|
|
|
tag = be32((u8*) &header->imd5_tag);
|
|
if (tag != 0x494D4435)
|
|
{
|
|
return -4;
|
|
}
|
|
|
|
md5(data + 32, size - 32, md5_calc);
|
|
if (memcmp(&header->md5, md5_calc, 0x10))
|
|
{
|
|
return -5;
|
|
}
|
|
|
|
size_in_imd5 = be32((u8*) &header->size);
|
|
if (size_in_imd5 != size - 32)
|
|
{
|
|
return -6;
|
|
}
|
|
|
|
tag = be32((u8*) &header->payload_tag);
|
|
if (tag == 0x4C5A3737)
|
|
{
|
|
// "LZ77" - uncompress
|
|
decompressed_data = decompress_lz77(data + sizeof(imd5_header_t), size - sizeof(imd5_header_t),
|
|
&decompressed_size);
|
|
if (decompressed_data == NULL) return -7;
|
|
write_file(decompressed_data, decompressed_size, outname);
|
|
//printf(", uncompressed %d bytes, md5 ok", decompressed_size);
|
|
|
|
free(decompressed_data);
|
|
}
|
|
else
|
|
{
|
|
write_file(&header->payload_tag, size - 32, outname);
|
|
//printf(", md5 ok");
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int do_U8_archive(FILE *fp)
|
|
{
|
|
U8_archive_header header;
|
|
U8_node root_node;
|
|
u32 tag;
|
|
u32 num_nodes;
|
|
U8_node* nodes;
|
|
u8* string_table;
|
|
size_t rest_size;
|
|
unsigned int i;
|
|
u32 data_offset;
|
|
u32 current_offset;
|
|
u16 dir_stack[16];
|
|
int dir_index = 0;
|
|
|
|
fread(&header, 1, sizeof header, fp);
|
|
tag = be32((u8*) &header.tag);
|
|
if (tag != 0x55AA382D)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
fread(&root_node, 1, sizeof(root_node), fp);
|
|
num_nodes = be32((u8*) &root_node.size) - 1;
|
|
//printf("Number of files: %d\n", num_nodes);
|
|
|
|
nodes = malloc(sizeof(U8_node) * (num_nodes));
|
|
fread(nodes, 1, num_nodes * sizeof(U8_node), fp);
|
|
|
|
data_offset = be32((u8*) &header.data_offset);
|
|
rest_size = data_offset - sizeof(header) - (num_nodes + 1) * sizeof(U8_node);
|
|
|
|
string_table = malloc(rest_size);
|
|
fread(string_table, 1, rest_size, fp);
|
|
current_offset = data_offset;
|
|
|
|
for (i = 0; i < num_nodes; i++)
|
|
{
|
|
U8_node* node = &nodes[i];
|
|
u16 type = be16((u8*) &node->type);
|
|
u16 name_offset = be16((u8*) &node->name_offset);
|
|
u32 my_data_offset = be32((u8*) &node->data_offset);
|
|
u32 size = be32((u8*) &node->size);
|
|
char* name = (char*) &string_table[name_offset];
|
|
u8* file_data;
|
|
|
|
if (type == 0x0100)
|
|
{
|
|
// Directory
|
|
mkdir(name, 0777);
|
|
chdir(name);
|
|
dir_stack[++dir_index] = size;
|
|
//printf("%*s%s/\n", dir_index, "", name);
|
|
}
|
|
else
|
|
{
|
|
// Normal file
|
|
u8 padding[32];
|
|
|
|
if (type != 0x0000)
|
|
{
|
|
free(string_table);
|
|
return -2;
|
|
}
|
|
|
|
if (current_offset < my_data_offset)
|
|
{
|
|
int diff = my_data_offset - current_offset;
|
|
|
|
if (diff > 32)
|
|
{
|
|
free(string_table);
|
|
return -3;
|
|
}
|
|
fread(padding, 1, diff, fp);
|
|
current_offset += diff;
|
|
}
|
|
|
|
file_data = malloc(size);
|
|
fread(file_data, 1, size, fp);
|
|
//printf("%*s %s (%d bytes", dir_index, "", name, size);
|
|
int result;
|
|
result = write_imd5_lz77(file_data, size, name);
|
|
if (result < 0)
|
|
{
|
|
free(string_table);
|
|
return result;
|
|
}
|
|
//printf(")\n");
|
|
current_offset += size;
|
|
}
|
|
|
|
while (dir_stack[dir_index] == i + 2 && dir_index > 0)
|
|
{
|
|
chdir("..");
|
|
dir_index--;
|
|
}
|
|
}
|
|
free(string_table);
|
|
return 0;
|
|
}
|
|
|
|
static void do_imet_header(FILE *fp)
|
|
{
|
|
imet_data_t header;
|
|
|
|
fread(&header, 1, sizeof header, fp);
|
|
|
|
write_file(&header, sizeof(header), "header.imet");
|
|
}
|
|
|
|
void do_U8_archivebanner(FILE *fp)
|
|
{
|
|
U8_archive_header header;
|
|
U8_node root_node;
|
|
u32 tag;
|
|
u32 num_nodes;
|
|
U8_node* nodes;
|
|
u8* string_table;
|
|
size_t rest_size;
|
|
unsigned int i;
|
|
u32 data_offset;
|
|
u16 dir_stack[16];
|
|
int dir_index = 0;
|
|
|
|
fread(&header, 1, sizeof header, fp);
|
|
tag = be32((u8*) &header.tag);
|
|
if (tag != 0x55AA382D)
|
|
{
|
|
//printf("No U8 tag");
|
|
exit(0);
|
|
}
|
|
|
|
fread(&root_node, 1, sizeof(root_node), fp);
|
|
num_nodes = be32((u8*) &root_node.size) - 1;
|
|
printf("Number of files: %d\n", num_nodes);
|
|
|
|
nodes = malloc(sizeof(U8_node) * (num_nodes));
|
|
fread(nodes, 1, num_nodes * sizeof(U8_node), fp);
|
|
|
|
data_offset = be32((u8*) &header.data_offset);
|
|
rest_size = data_offset - sizeof(header) - (num_nodes + 1) * sizeof(U8_node);
|
|
|
|
string_table = malloc(rest_size);
|
|
fread(string_table, 1, rest_size, fp);
|
|
|
|
for (i = 0; i < num_nodes; i++)
|
|
{
|
|
U8_node* node = &nodes[i];
|
|
u16 type = be16((u8*) &node->type);
|
|
u16 name_offset = be16((u8*) &node->name_offset);
|
|
u32 my_data_offset = be32((u8*) &node->data_offset);
|
|
u32 size = be32((u8*) &node->size);
|
|
char* name = (char*) &string_table[name_offset];
|
|
u8* file_data;
|
|
|
|
if (type == 0x0100)
|
|
{
|
|
// Directory
|
|
mkdir(name, 0777);
|
|
chdir(name);
|
|
dir_stack[++dir_index] = size;
|
|
//printf("%*s%s/\n", dir_index, "", name);
|
|
}
|
|
else
|
|
{
|
|
// Normal file
|
|
|
|
if (type != 0x0000)
|
|
{
|
|
printf("Unknown type");
|
|
exit(0);
|
|
}
|
|
|
|
fseek(fp, my_data_offset, SEEK_SET);
|
|
file_data = malloc(size);
|
|
fread(file_data, 1, size, fp);
|
|
write_file(file_data, size, name);
|
|
free(file_data);
|
|
//printf("%*s %s (%d bytes)\n", dir_index, "", name, size);
|
|
}
|
|
|
|
while (dir_stack[dir_index] == i + 2 && dir_index > 0)
|
|
{
|
|
chdir("..");
|
|
dir_index--;
|
|
}
|
|
}
|
|
free(string_table);
|
|
}
|
|
|
|
int extractbnrfile(const char * filepath, const char * destpath)
|
|
{
|
|
int ret = -1;
|
|
FILE *fp = fopen(filepath, "rb");
|
|
if (fp)
|
|
{
|
|
CreateSubfolder(destpath);
|
|
chdir(destpath);
|
|
|
|
do_imet_header(fp);
|
|
ret = do_U8_archive(fp);
|
|
|
|
fclose(fp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int unpackBin(const char * filename, const char * outdir)
|
|
{
|
|
FILE *fp = fopen(filename, "rb");
|
|
;
|
|
if (fp)
|
|
{
|
|
CreateSubfolder(outdir);
|
|
chdir(outdir);
|
|
|
|
do_U8_archivebanner(fp);
|
|
fclose(fp);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
|
|
#define TMP_PATH(s) "BANNER:/dump"s
|
|
//#define TMP_PATH(s) "SD:/dump"s
|
|
|
|
int unpackBanner(const u8 *gameid, int what, const char *outdir)
|
|
{
|
|
|
|
char path[256];
|
|
if (!ramdiskMount("BANNER", NULL)) return -1;
|
|
|
|
CreateSubfolder(TMP_PATH( "/" ));
|
|
s32 ret = dump_banner(gameid, TMP_PATH( "/opening.bnr" ));
|
|
if (ret != 1)
|
|
{
|
|
ret = -1;
|
|
goto error2;
|
|
}
|
|
|
|
ret = extractbnrfile(TMP_PATH( "/opening.bnr" ), TMP_PATH( "/" ));
|
|
if (ret != 0)
|
|
{
|
|
ret = -1;
|
|
goto error2;
|
|
}
|
|
|
|
if (what & UNPACK_BANNER_BIN)
|
|
{
|
|
snprintf(path, sizeof(path), "%sbanner/", outdir);
|
|
ret = unpackBin(TMP_PATH( "/meta/banner.bin" ), path);
|
|
if (ret != 1)
|
|
{
|
|
ret = -1;
|
|
goto error2;
|
|
}
|
|
}
|
|
if (what & UNPACK_ICON_BIN)
|
|
{
|
|
snprintf(path, sizeof(path), "%sicon/", outdir);
|
|
ret = unpackBin(TMP_PATH( "/meta/icon.bin" ), path);
|
|
if (ret != 1)
|
|
{
|
|
ret = -1;
|
|
goto error2;
|
|
}
|
|
}
|
|
if (what & UNPACK_SOUND_BIN)
|
|
{
|
|
snprintf(path, sizeof(path), "%ssound.bin", outdir);
|
|
FILE *fp = fopen(TMP_PATH( "/meta/sound.bin" ), "rb");
|
|
if (fp)
|
|
{
|
|
size_t size;
|
|
u8 *data;
|
|
fseek(fp, 0, SEEK_END);
|
|
size = ftell(fp);
|
|
if (!size)
|
|
{
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
fseek(fp, 0, SEEK_SET);
|
|
data = (u8 *) malloc(size);
|
|
if (!data)
|
|
{
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
if (fread(data, 1, size, fp) != size)
|
|
{
|
|
ret = -1;
|
|
goto error;
|
|
}
|
|
ret = write_file(data, size, path);
|
|
}
|
|
error: fclose(fp);
|
|
}
|
|
ramdiskUnmount("BANNER");
|
|
error2: if (ret < 0) return ret;
|
|
return 1;
|
|
}
|
|
#endif
|