Last active
March 31, 2020 20:41
-
-
Save flibitijibibo/ed120d4a60c91c466735 to your computer and use it in GitHub Desktop.
I wrote this because I really like Sonic Adventure 2, and I take games I like apart, nowadays to see how portable they might be. The shaders found in the PC version can be read and rebuilt by MojoShader! So that'd settle that part of it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* SA2UNPAK - Sonic Adventure 2 PC Shader obj.pak Extraction Program | |
* Written by Ethan "flibitijibibo" Lee | |
* http://www.flibitijibibo.com/ | |
* | |
* Released under public domain. | |
* No warranty implied; use at your own risk. | |
* | |
* I wrote this to extract the D3D9 shader binary files for parsing | |
* in MojoShader. This may not work with any other file in the game. | |
* Also, I am not porting SA2 to Linux. Though I really would like to. | |
*/ | |
#include <stdio.h> | |
#include <stdint.h> | |
#include <stdlib.h> | |
#include <string.h> | |
/* obj.pak filesize */ | |
#define PAK_SIZE 22461 | |
/* Max path size, arbitrarily Win32 MAX_PATH */ | |
#define PATH_SIZE 260 | |
/* Quick macro to dump message to stdout and exit */ | |
#define FAIL(msg) \ | |
printf("%s\n", msg); \ | |
return 0; | |
/* Quick macro to free all strings and the string array */ | |
#define FREE_STRINGS \ | |
for (i = 0; i < numFiles; i += 1) \ | |
{ \ | |
free(filePaths[i]); \ | |
} \ | |
free(filePaths); | |
/* Quick macro to get a uint32_t value from the location */ | |
#define LOC32 ((uint32_t*) location) | |
int main(int argc, char **argv) | |
{ | |
/* pak reading variables */ | |
FILE *file; | |
char data[PAK_SIZE]; | |
char *location = data; /* Marker for data reading */ | |
/* Stores info about the actual "files" */ | |
uint32_t numFiles; | |
uint32_t totalSize; | |
uint32_t totalSizeCheck; | |
uint32_t *fileSizes; | |
char **filePaths; | |
/* Used to get/print the virtual file paths */ | |
uint32_t numChars; | |
char path[PATH_SIZE]; | |
/* Used to check for Win32 path separators */ | |
char *pathSeparator; | |
/* Every program has one of these! */ | |
uint32_t i; | |
/* Read pak file to data block */ | |
file = fopen("obj.pak", "rb"); | |
if (file == NULL) | |
{ | |
FAIL("File failed to open!") | |
} | |
fread(data, PAK_SIZE, 1, file); | |
fclose(file); | |
/* File header is ".pak" */ | |
if (*LOC32 != 0x6B617001) | |
{ | |
FAIL(".pak header invalid!") | |
} | |
location += 5; /* Null terminator! */ | |
/* Empty bytes...? */ | |
location += 32; | |
/* Number of files, total size of files */ | |
numFiles = LOC32[0]; | |
totalSize = LOC32[1]; | |
totalSizeCheck = 0; | |
fileSizes = (uint32_t*) malloc(numFiles * sizeof(uint32_t)); | |
filePaths = (char**) malloc(numFiles * sizeof(void*)); | |
location += 8; | |
/* Total size again? */ | |
location += 4; | |
/* Empty bytes...? */ | |
location += 8; | |
/* Numer of files again? */ | |
location += 4; | |
/* Iterate through file info block */ | |
for (i = 0; i < numFiles; i += 1) | |
{ | |
/* Size of path string */ | |
numChars = *LOC32; | |
location += 4; | |
/* Print full path. Notice: No null terminators provided! */ | |
memcpy(path, location, numChars); | |
path[numChars] = '\0'; | |
printf("File %i real path: %s\n", i, path); | |
/* That's the first of two strings... */ | |
location += numChars; | |
/* ... then there's another, but it's just a simple version of | |
* the first path we found. We'll use this for file output. | |
*/ | |
numChars = *LOC32; | |
location += 4; | |
/* Copy the string, add a null terminator! */ | |
filePaths[i] = (char*) malloc(numChars + 1); | |
memcpy(filePaths[i], location, numChars); | |
(filePaths[i])[numChars] = '\0'; | |
/* For non-Windows, replace directory separators */ | |
pathSeparator = strstr(filePaths[i], "\\"); | |
while (pathSeparator != NULL) | |
{ | |
*pathSeparator = '/'; | |
pathSeparator = strstr(filePaths[i], "\\"); | |
} | |
/* Right, that's both strings settled. */ | |
location += numChars; | |
/* Store file size */ | |
fileSizes[i] = *LOC32; | |
totalSizeCheck += fileSizes[i]; | |
location += 4; | |
/* File size again...? */ | |
location += 4; | |
} | |
/* All of the sizes we just got should add up to our total! */ | |
if (totalSizeCheck != totalSize) | |
{ | |
free(fileSizes); | |
FREE_STRINGS | |
FAIL("Total size verification failed!") | |
} | |
/* Now iterate through the actual files, dumping to file. */ | |
for (i = 0; i < numFiles; i += 1) | |
{ | |
/* FIXME: Assumes you have the full directory tree made! */ | |
file = fopen(filePaths[i], "wb"); | |
if (file == NULL) | |
{ | |
free(fileSizes); | |
FREE_STRINGS | |
FAIL("Failed to open output file!") | |
} | |
fwrite(location, fileSizes[i], 1, file); | |
fclose(file); | |
location += fileSizes[i]; | |
printf("Wrote file %s\n", filePaths[i]); | |
} | |
/* Clean up. We out. */ | |
free(fileSizes); | |
FREE_STRINGS | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment