Last active
April 6, 2025 16:05
-
-
Save hayleyxyz/0c344bddb8fb570f83069b283a7b6a47 to your computer and use it in GitHub Desktop.
Indiana Jones and the Great Circle .resource extractor
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
using System.Text; | |
namespace indyresources | |
{ | |
internal class Program | |
{ | |
static void Main(string[] args) | |
{ | |
ParseResources(File.OpenRead(@"D:\SteamLibrary\steamapps\common\The Great Circle\base\gameresources_pc.resources")); | |
} | |
struct Entry | |
{ | |
public ulong[] values; | |
public string name; | |
public long Offset => (long) values[6]; | |
public int Size => (int) values[7]; | |
} | |
static void ParseResources(Stream stream) | |
{ | |
var entries = new List<Entry>(); | |
var stringOffsets = new List<ulong>(); | |
var stringTable = new List<string>(); | |
var block = new byte[0x90]; | |
// Read header | |
stream.Position = 0; | |
stream.Read(block, 0, block.Length); | |
for (int i = 0; i < block.Length / 4; i++) | |
{ | |
uint value = BitConverter.ToUInt32(block, i * 4); | |
Console.WriteLine(value.ToString("x8")); | |
} | |
// 0x24 in header is number of blocks | |
stream.Position = 0x24; | |
stream.Read(block, 0, 8); | |
var numberOfBlocks = BitConverter.ToInt32(block, 0); | |
Console.WriteLine($"Number of blocks: {numberOfBlocks} 0x{numberOfBlocks.ToString("x")}"); | |
// 0x30 is the number of entries after the strings | |
stream.Position = 0x30; | |
stream.Read(block, 0, 8); | |
var numberOfPostStringEntries = BitConverter.ToInt32(block, 0); | |
Console.WriteLine($"Number of post-string entries: {numberOfPostStringEntries} 0x{numberOfPostStringEntries.ToString("x")}"); | |
stream.Position = 0x90; | |
for (var i = 0; i < numberOfBlocks; i++) | |
{ | |
var values = new ulong[block.Length / 8]; | |
stream.Read(block, 0, block.Length); | |
for (var x = 0; x < values.Length; x++) | |
{ | |
values[x] = BitConverter.ToUInt64(block, x * 8); | |
} | |
foreach (var item in values) | |
{ | |
Console.Write(item.ToString("x16")); | |
Console.Write(' '); | |
} | |
var entry = new Entry(); | |
entry.values = values; | |
entries.Add(entry); | |
Console.WriteLine(); | |
} | |
Console.WriteLine($"End position of last block: {stream.Position}"); | |
// Number of (u)int64s are stored in the last block | |
var numberOfStringOffsets = entries.Last().values[17]; | |
Console.WriteLine($"Number of string offsets: {numberOfStringOffsets} 0x{numberOfStringOffsets.ToString("x")}"); | |
for (var i = 0u; i < numberOfStringOffsets; i++) | |
{ | |
stream.Read(block, 0, 8); | |
var value = BitConverter.ToUInt64(block, 0); | |
Console.WriteLine($"String offset {i}: {value} 0x{value.ToString("x")}"); | |
stringOffsets.Add(value); | |
} | |
Console.WriteLine($"End position of last value: {stream.Position}"); | |
var numberOfStrings = numberOfStringOffsets; | |
var baseStringOffset = stream.Position; | |
foreach (var offset in stringOffsets) | |
{ | |
stream.Position = baseStringOffset + (long) offset; | |
var str = ReadNullTerminatedString(stream); | |
Console.WriteLine(str); | |
stringTable.Add(str); | |
} | |
Console.WriteLine($"End position of last string value: {stream.Position}"); | |
// Round the position to the next 8-byte boundary | |
stream.Position = (stream.Position + 7) & ~7; | |
for (var i = 0u; i < numberOfPostStringEntries; i++) | |
{ | |
stream.Read(block, 0, 8); | |
var value = BitConverter.ToUInt64(block, 0); | |
Console.WriteLine($"Post-string entry {i}: {value} 0x{value.ToString("x")}"); | |
} | |
Console.WriteLine($"End position of last value: {stream.Position}"); | |
int index = 1; | |
foreach (var entry in entries) | |
{ | |
stream.Position = entry.Offset; | |
var bytes = new byte[entry.Size]; | |
stream.Read(bytes, 0, entry.Size); | |
if (! Directory.Exists("extracted")) | |
Directory.CreateDirectory("extracted"); | |
var ourFilePath = "extracted/" + (index++) + "-" + entry.Offset.ToString("x16") + ".bin"; | |
File.WriteAllBytes(ourFilePath, bytes); | |
} | |
} | |
private static string ReadNullTerminatedString(Stream stream) | |
{ | |
List<byte> bytes = new List<byte>(); | |
int b; | |
while ((b = stream.ReadByte()) != -1) | |
{ | |
if (b == 0x00) // Null terminator found | |
break; | |
bytes.Add((byte) b); | |
} | |
if (b == -1 && bytes.Count == 0) | |
throw new Exception("Invalid string at position " + stream.Position); | |
return Encoding.UTF8.GetString(bytes.ToArray()); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment