
/*
	wiiisoinfo.c - 08012006 (gpz)
	
	(doesnt it look depressing? :=P)
*/

// undef when building on a big endian arch
//#define HOST_BIG_ENDIAN

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

/*
	note: values marked [BE] are in big-endian format on disk
*/

// disk header info at offset 0x00000000
typedef struct
{
	uint8_t 	gamecode[4];		// 0x00
	uint8_t 	makercode[2];		// 0x04
	uint8_t		unk_6[0x12];		// 0x06 (zeros)
	uint32_t	unk_18;			// 0x18 (DVD Magic ?, always a39e1c5d ?)
	uint32_t	unk_1c;			// 0x1c (zeros, reserved for GC DVD Magic ?)
	uint8_t		gamename[0x3e0];	// 0x20
} DISKHEADER;

// TOC at 0x00040000
typedef struct
{
	uint32_t	numpart;		// 0x00 [BE] number of partitions (==2)
	uint32_t	partoffs;		// 0x04 [BE] partition info offset >> 2  (==0x00040020)
	uint32_t	unk[6];			// 0x08 zeros
} DISKTOC;

// Partition info, usually at 0x00040020 (8 bytes per partition)
typedef struct
{
	uint32_t	partoffs;		// 0x00 [BE] partition offset >> 2 (0x00050000;0f800000)
	uint32_t	parttype;		// 0x04 [BE] partition type (1=?, 0=game boot)
} DISKPARTINFO;


/*
 0004e000 (?)
 0004e000 00 00 00 01  00 00 00 00  00 00 00 00  00 00 00 00  80 0d 80 80  80 80 80 80  80 80 80 80  80 80 80 80

 0004fff8 (?, same on all dumps)
 0004ffe0 00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  c3 f8 1a 8e

each partition begins with a block of certificates (?)
partition-start +0x20000 is begin of (encrypted) filesystem (?)

first partition
 00050000 (RSA? certificates?, same on all dumps)

 00058000 (?)
 00058000 7d 74 d7 af  8d 17 cc c9  1c 16 fa 2b  09 c7 f8 3e  e1 4f 85 8d  00 00 00 00  00 00 00 00  00 00 00 00

 00070000-00260000 (encrypted file data ?)

second partition (game boot)		
 0f800000 (RSA? certificates?)
 0f820000-18240000 (encrypted file data ?)
*/		

/*
note: 

if you see data at offset 00002800 which looks like plaintext code, don't be fooled. the disk was (obviously partially)
dumped on a gamecube, and what you are looking at is the gamecube apploader injected by the xenogc modchip. real wii 
disks do _not_ contain any data at this offset (atleast not the dumps i've been looking at so far).
*/

typedef struct
{
  char letter;
  char *string;
} ID;
typedef struct
{
  char letter1;
  char letter2;
  char *string;
} ID2;

ID consolecodes[] =
{
	{ 'F', "VC - NES" }, 
	{ 'J', "VC - SNES" },
	{ 'M', "VC - Genesis" },
	{ 'N', "VC - N64" },
	{ 'P', "VC - TurboGraphx 16" },
	{ 'R', "Wii" },
	{ 'H', "Wii Channel" },
	{  0, NULL }
};

ID countrycodes[] = 
{
	{'E', "NTSC/USA" },
	{'J', "NTSC/Japan" },
	{'P', "PAL/Europe" },
	{ 0 , NULL }
};

// moooh, too lazy to finish this list now =P
ID2 gamecodes[] = 
{
// (R) WII
	{'C','D', "Call of Duty 3" },
	{'R','B', "Rayman - Raving Rabbits" },
	{'E','D', "Red Steel" },
	{'H','A', "Wii Play" },
	{'S','P', "Wii Sports" },
		
// (F) NES
	{'A','F', "Mario Bros" },
	{'A','G', "Super Mario Bros." },
	{'A','K', "The Legend of Zelda" },
	
// (J) SNES
	{'A','C', "F-Zero" },
	{'A','F', "SimCity" },
	
// (M) Genesis
	{'A','H', "Sonic the Hedgehog" },
	
// (N) N64
	{'A','A', "Super Mario 64" },
	
// (P) TurboGraphx 16
	{'A','A', "Bomberman '93" },
	
//	{'?','?', "Alien Crush" },
//	{'?','?', "Alien Syndrome" },
//	{'?','?', "Altered Beast" },
//	{'?','?', "Animal Crossing" },
//	{'?','?', "The Ant Bully" },
//	{'?','?', "Avatar: The Last Airbender" },
//	{'?','?', "Bachindaa" },
//	{'?','?', "Barnyard" },
//	{'?','?', "Baseball" },
//	{'?','?', "Battalion Wars 2" },
//	{'?','?', "Big Brain Academy (working title)" },
//	{'?','?', "BIGS, The" },
//	{'?','?', "Bio-Hazard Battle" },
//	{'?','?', "Biohazard Umbrella Chronicles" },
//	{'?','?', "Blazing Angels" },
//	{'?','?', "Blazing Angels: Squadrons of WWII" },
//	{'?','?', "Bleach Wii: Hakujin Kirameku Rondo" },
//	{'?','?', "Blitz: The League" },
//	{'?','?', "Bob Ross Painting" },
//	{'?','?', "Boku to Sim no Machi" },
//	{'?','?', "Bokujou Monogatari: Yasuragi no Ki" },
//	{'?','?', "Bomberman Land" },
//	{'?','?', "Bomberman Land Wii" },
//	{'?','?', "Bonanza Bros" },
//	{'?','?', "Bonk's Adventure" },
//	{'?','?', "Bust-A-Move Bash!" },
//	{'?','?', "Caduceus Z: 2-tsu no Chou Shittou" },
//	{'?','?', "California Games" },
//	{'?','?', "Cars" },
//	{'?','?', "Columns" },
//	{'?','?', "Comix Zone" },
//	{'?','?', "Contra III: The Alien Wars" },
//	{'?','?', "Cooking Mama: Cook Off" },
//	{'?','?', "Cooking Mama: Minna to Issho ni Oryouri Taikai!" },
//	{'?','?', "Crash Dummy vs. the Evil D-Troit" },
//	{'?','?', "Crayon Shin-Chan: Saikyou Kazoku Kasukabe King Wii" },
//	{'?','?', "Crossword" },
//	{'?','?', "Dairantou Smash Brothers X" },
//	{'?','?', "Dance Factory" },
//	{'?','?', "Densha de Go! Shinkansen EX: Sanyou Shinkansen Hen" },
//	{'?','?', "Digimon" },
//	{'?','?', "Disaster: Day of Crisis" },
//	{'?','?', "Disney's Chicken Little: Ace in Action" },
//	{'?','?', "Disney's Meet the Robinsons" },
//	{'?','?', "DK Bongo Blast" },
//	{'?','?', "Dog Island, The" },
//	{'?','?', "Dog Island: Hitotsu no Hana no Monogatari, The" },
//	{'?','?', "Donkey Kong" },
//	{'?','?', "Donkey Kong" },
//	{'?','?', "Donkey Kong Jr." },
//	{'?','?', "Donkey Kong Taru Jet Race" },
//	{'?','?', "Double Dungeons" },
//	{'?','?', "Doubutsu no Mori" },
//	{'?','?', "Dr. Robotnik's Mean Bean Machine" },
	{'D','B', "Dragon Ball Z: Budokai Tenkaichi 2" },
//	{'?','?', "Dragon Ball Z: Sparking! Neo" },
//	{'?','?', "Dragon Quest Sword: Kamen no Joou to Kagami no Tou" },
//	{'?','?', "Dragon Quest Swords: The Masked Queen and the Tower of Mirrors" },
//	{'?','?', "Driver: Parallel Lines" },
//	{'?','?', "Dungeon Explorer" },
//	{'?','?', "Ecco the Dolphin" },
//	{'?','?', "Elebits" },
//	{'?','?', "Eledees" },
//	{'?','?', "Ennichi no Tatsujin" },
//	{'?','?', "Escape from Bug Island" },
//	{'?','?', "EX	Excite Truck" },
//	{'?','?', "Eyeshield 21: Field no Saikyou Senshi Tachi" },
//	{'?','?', "Far Cry Vengeance" },
//	{'?','?', "Final Fantasy Crystal Chronicles: The Crystal Bearers" },
//	{'?','?', "Final Furlong" },
//	{'?','?', "Fire Emblem" },
//	{'?','?', "Fire Emblem: Akatsuki no Megami" },
//	{'?','?', "Flight Game" },
//	{'?','?', "Forever Blue" },
//	{'?','?', "Furu Furu Park" },
//	{'?','?', "G1 Jockey 4" },
//	{'?','?', "Gain Ground" },
//	{'?','?', "Godfather, The" },
//	{'?','?', "Godfather: Blackhand Edition, The" },
//	{'?','?', "Golden Axe" },
//	{'?','?', "Gottlieb Pinball Classics" },
//	{'?','?', "Gradius" },
//	{'?','?', "Grim Adventures of Billy & Mandy, The" },
//	{'?','?', "GT Pro Series" },
//	{'?','?', "GT Wii" },
//	{'?','?', "Gunstar Heroes" },
//	{'?','?', "Hajime no Ippo Revolution" },
//	{'?','?', "Hajimete no Wii" },
//	{'?','?', "Happy Feet" },
//	{'?','?', "Harry Potter" },
//	{'?','?', "Harry Potter and the Order of the Phoenix" },
//	{'?','?', "Health Pack" },
//	{'?','?', "Heatseeker" },
//	{'?','?', "High School Musical" },
//	{'?','?', "Hoshi no Kirby (Working Title)" },
//	{'?','?', "Ice Age 2: The Meltdown" },
//	{'?','?', "Ice Hockey" },
//	{'?','?', "Impossible Mission" },
//	{'?','?', "Jawa" },
//	{'?','?', "Jigsaw Puzzle" },
//	{'?','?', "Kateikyoushi Hitman Reborn!" },
//	{'?','?', "Kawa No Nushi Tsuri" },
//	{'?','?', "Kororinpa" },
//	{'?','?', "Last Ninja, The" },
//	{'?','?', "Leaderboard Golf" },
//	{'?','?', "Legend of the Dragon" },
//	{'?','?', "Legend of Zelda: Ocarina of Time, The" },
	{'Z','D', "The Legend of Zelda - Twilight Princess" },
//	{'?','?', "Lewis to Mirai Dorobou" },
//	{'?','?', "Line Rider" },
//	{'?','?', "Machikuru Domino" },
//	{'?','?', "Madden NFL 07" },
//	{'?','?', "Mahjong Taikai Wii" },
//	{'?','?', "Mario Kart 64" },
//	{'?','?', "Mario Party 8" },
//	{'?','?', "Mario Strikers Charged" },
//	{'?','?', "Marvel: Ultimate Alliance" },
//	{'?','?', "Medal of Honor: Airborne" },
//	{'?','?', "Medal of Honor: Vanguard" },
//	{'?','?', "Meitantei Conan: Tsuioku no Gensou" },
//	{'?','?', "Mercury Meltdown Revolution" },
//	{'?','?', "Metal Slug Anthology" },
//	{'?','?', "Metroid Prime 3: Corruption" },
//	{'?','?', "Mezase!! Tsuri Master" },
//	{'?','?', "Military Madness" },
//	{'?','?', "Monster 4X4: World Circuit" },
//	{'?','?', "Mortal Kombat: Armageddon" },
//	{'?','?', "Motto! Mawasunda!!" },
//	{'?','?', "Mr. D. Goes to Town" },
//	{'?','?', "Naruto Shippuuden: Gekitou Ninja Taisen EX" },
//	{'?','?', "Necro-Nesia" },
//	{'?','?', "Need for Speed Carbon" },
//	{'?','?', "New Adventure Island" },
//	{'?','?', "No More Heroes" },
//	{'?','?', "No-Kone Puzzle: Takoron" },
//	{'?','?', "Odoru Made in Wario" },
//	{'?','?', "One Piece Unlimited Adventure" },
//	{'?','?', "Open Season" },
//	{'?','?', "Pac-Man Carnival" },
//	{'?','?', "Pinball" },
//	{'?','?', "Pirates of the Caribbean 3" },
//	{'?','?', "Pokemon Battle Revolution" },
//	{'?','?', "Prince of Persia Rival Swords" },
//	{'?','?', "Princess Company" },
//	{'?','?', "Project H.A.M.M.E.R." },
//	{'?','?', "Puzzle Bobble Bash!" },
//	{'?','?', "Puzzle Series Vol. 1: Sudoku" },
//	{'?','?', "R-Type" },
//	{'?','?', "Rabbids Party" },
//	{'?','?', "Rampage: Total Destruction" },
//	{'?','?', "Rapala Tournament Fishing" },
//	{'?','?', "Ratatouille" },
//	{'?','?', "Resident Evil" },
//	{'?','?', "Resident Evil: Umbrella Chronicles" },
//	{'?','?', "Ristar" },
//	{'?','?', "Sadness" },
//	{'?','?', "San Goku Shi XI with Power-Up Kit" },
//	{'?','?', "SAN-X All-Star Revolution" },
//	{'?','?', "SD Gundam: Scad Hammers" },
//	{'?','?', "Sengoku Basara" },
//	{'?','?', "Sengoku Musou Wave" },
//	{'?','?', "Shooting Tech Demo" },
//	{'?','?', "Shrek Smash and Crash Racing" },
//	{'?','?', "Shrek the Third" },
//	{'?','?', "Sidewinder" },
//	{'?','?', "SIMPLE Series" },
//	{'?','?', "Sims (working title), The" },
//	{'?','?', "Soccer" },
//	{'?','?', "Soldier Blade" },
//	{'?','?', "Solomon's Key" },
//	{'?','?', "Sonic and the Secret Rings" },
//	{'?','?', "Sonic to Himitsu Ring" },
//	{'?','?', "Space Harrier II" },
//	{'?','?', "Spider-Man 3" },
//	{'?','?', "SpongeBob" },
//	{'?','?', "SpongeBob SquarePants: Creature from the Krusty Krab" },
//	{'?','?', "SSX" },
//	{'?','?', "SSX Blur" },
//	{'?','?', "Street Fighter II: The World Warrior" },
//	{'?','?', "Streets of Rage" },
//	{'?','?', "Sudoku" },
//	{'?','?', "Super Castlevania IV" },
//	{'?','?', "Super Fruit Fall" },
//	{'?','?', "Super Mario Galaxy" },
//	{'?','?', "Super Mario World" },
	{'S','M', "Super Monkey Ball" },
//	{'?','?', "Super Monkey Ball: Banana Blitz" },
//	{'?','?', "Super Monkey Ball: Uki Uki Party Daishuugou" },
//	{'?','?', "Super Paper Mario" },
//	{'?','?', "Super Probotector" },
//	{'?','?', "Super Smash Bros. Brawl" },
//	{'?','?', "Super Star Soldier" },
//	{'?','?', "Super Swing Golf" },
//	{'?','?', "Swing Golf Pangya" },
//	{'?','?', "Sword of Legendia" },
//	{'?','?', "Tamagotchi" },
//	{'?','?', "Tamagotchi no Pika Pika Daitouryou!" },
//	{'?','?', "Tennis" },
//	{'?','?', "Tennis Masters" },
//	{'?','?', "Tiger Woods PGA Tour 07" },
//	{'?','?', "TMNT" },
//	{'?','?', "TNA iMPACT!" },
//	{'?','?', "ToeJam & Earl" },
//	{'?','?', "Tom Clancy's Splinter Cell Double Agent" },
//	{'?','?', "Tony Hawk's Downhill Jam" },
//	{'?','?', "Totsugeki Famicom Wars W" },
	{'K','D', "Trauma Center: Second Opinion" },
//	{'?','?', "Turn It Around!!" },
//	{'?','?', "Urban Champion" },
//	{'?','?', "Victory Run" },
//	{'?','?', "Wario's Woods" },
//	{'?','?', "WarioWare: Smooth Moves" },
//	{'?','?', "Wii Channels" },
//	{'?','?', "Wii Music" },
//	{'?','?', "Wii Music Orchestra" },
//	{'?','?', "Wing Island" },
//	{'?','?', "World Championship Poker: Featuring Howard Lederer - All In" },
//	{'?','?', "World Series of Poker: Tournament of Champions" },

	{ 0 , 0, NULL }
};

ID2 makercodes[] = 
{
	{'0','1', "Nintendo" },
	{'0','8', "Capcom" },
	{'4','1', "Ubisoft" },
	{'4','F', "Eidos" },
	{'5','1', "Acclaim" },
	{'5','2', "Activision" },
	{'5','D', "Midway" },
	{'5','G', "Hudson" },
	{'6','4', "Lucas Arts" },
	{'6','9', "Electronic Arts" },
	{'6','S', "TDK Mediactive" },
	{'8','P', "Sega" },
	{'A','4', "Mirage Studios" },
	{'A','F', "Namco" },
	{'B','2', "Bandai" },
	{'D','A', "Tomy" },
	{'E','M', "Konami" },
	{ 0 , 0, NULL }
};

DISKHEADER	hdr;
DISKTOC		toc;
DISKPARTINFO	partinfo[0x10]; // 16 partitions should be more than enough =P
char *parttypes[2]={"game boot","unknown"};

FILE *f=NULL;

uint32_t swap32(uint32_t n)
{
#ifdef HOST_BIG_ENDIAN
	return n;
#else
	uint8_t *b=(uint8_t*)&n;
	return (((uint32_t)b[0])<<24)|(((uint32_t)b[1])<<16)|(((uint32_t)b[2])<<8)|(((uint32_t)b[3])<<0);
#endif	
}

char *findcode(ID *id,char n)
{
	while(id->string)
	{
		if(id->letter==n) return id->string;
		id++;
	}
	return "unknown";
}

char *findcode2(ID2 *id,char n1,char n2)
{
	while(id->string)
	{
		if((id->letter1==n1)&&(id->letter2==n2)) return id->string;
		id++;
	}
	return "unknown";
}

int readheader(void)
{
	fseek(f,0L,SEEK_SET);
	fread(&hdr,sizeof(DISKHEADER),1,f);
	hdr.unk_18=swap32(hdr.unk_18);
}

int readtoc(void)
{
	fseek(f,0x40000L,SEEK_SET);
	fread(&toc,sizeof(DISKTOC),1,f);
	toc.partoffs=swap32(toc.partoffs)<<2;
	toc.numpart=swap32(toc.numpart);
}

int readpartinfo(DISKTOC *toc)
{
int i;	
	fseek(f,toc->partoffs,SEEK_SET);
	for(i=0;i<toc->numpart;i++)
	{
		fread(&partinfo[i],sizeof(DISKPARTINFO),1,f);
		partinfo[i].partoffs=swap32(partinfo[i].partoffs)<<2;
		partinfo[i].parttype=swap32(partinfo[i].parttype);
	}
}

int main(int argc,char *argv[])
{
int i;	
	f=fopen(argv[1],"rb");
	if(f!=NULL)
	{	
		readheader();
		readtoc();
		readpartinfo(&toc);
		fclose(f);
		
		printf("Info for %s:\n",argv[1]);
		printf("\n");
		printf("Gamecode: %c%c%c%c\n",hdr.gamecode[0],hdr.gamecode[1],hdr.gamecode[2],hdr.gamecode[3]);
		printf(" Console-ID:  %c (%s)\n",hdr.gamecode[0],findcode(consolecodes,hdr.gamecode[0]));
		printf(" Gamecode:    %c%c (%s)\n",hdr.gamecode[1],hdr.gamecode[2],findcode2(gamecodes,hdr.gamecode[1],hdr.gamecode[2]));
		printf(" Countrycode: %c (%s)\n",hdr.gamecode[3],findcode(countrycodes,hdr.gamecode[3]));
		printf("Makercode:    %c%c (%s)\n",hdr.makercode[0],hdr.makercode[1],findcode2(makercodes,hdr.makercode[0],hdr.makercode[1]));
		printf("unk_18:       0x%08x\n",hdr.unk_18);
		printf("Gamename:     %s\n",hdr.gamename);
		printf("\n");
		printf("Number of Partitions:     %d\n",toc.numpart);
		printf("Partition-Info Offset:    0x%08x\n",toc.partoffs);
		printf("\n");
		for(i=0;i<toc.numpart;i++)
		{
			printf("Partition #%d: ",i);
			printf("Offset 0x%08x ",partinfo[i].partoffs);
			printf("Type: %d (%s) ",partinfo[i].parttype,parttypes[partinfo[i].parttype]);
			printf("\n");
		}
	}
	else
	{
		printf("error opening '%s'\n",argv[1]);
	}
	
	return EXIT_SUCCESS;
}



