Skip to content

Instantly share code, notes, and snippets.

@flibitijibibo
Last active March 31, 2020 20:41
Show Gist options
  • Save flibitijibibo/ed120d4a60c91c466735 to your computer and use it in GitHub Desktop.
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.
/* 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