User:Magicus/Magicus's Tools/Parse-u8.c

From WiiBrew
Jump to navigation Jump to search

Put this file in the same directory as a compiled version of Segher's Wii.git tools, since it depends on them.

// parse-u8.c
// Compile with:
// gcc -g -DLARGE_FILES -D_FILE_OFFSET_BITS=64 -Wall -W -O2   -c -o parse-u8.o parse-u8.c
// gcc -g -lcrypto  parse-u8.o tools.o bn.o ec.o   -o parse-u8
// The other files are from segher's git repository, created by his Makefile.

// 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 0.5 Beta version, just lists contents, doesn't yet extract files.
// Version 1.0 Now extract files and re-create directory structure.

#define _GNU_SOURCE 

#include <sys/stat.h>
#include <sys/types.h>
       
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>

#include "tools.h"

#define ERROR(s) do { fprintf(stderr, s "\n"); exit(1); } while (0)

static FILE *fp;

static char *outdir;

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 void write_file(void* data, size_t size, char* name)
{
	FILE *out;

	out = fopen(name, "wb");
	fwrite(data, 1, size, out);
	fclose(out);	
}


static void do_U8_archive(void)
{
  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) {
	  ERROR("No U8 tag");
	}

	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) {
         ERROR("Unknown type");
      }

      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--;
    }
	}
}

int main(int argc, char **argv)
{
  char outdir_name[128];

  if (argc == 3) {
    outdir = argv[2];
  } else if (argc != 2) {
    ERROR("Usage: parse-u8 <file> [<outdir>]");
  } else {

    snprintf(outdir_name, sizeof(outdir_name), "%s.unpacked", basename(argv[1]));
    outdir_name[127] = '\0';
    outdir = outdir_name;
  }

  printf("Extracting files to %s.\n", outdir);
	fp = fopen(argv[1], "rb");

  mkdir(outdir, 0777);
  chdir(outdir);
  
  do_U8_archive();

	fclose(fp);

	return 0;
}