Skip to content

Commit 38a3e3b

Browse files
committed
Initial commit
1 parent 043f8c9 commit 38a3e3b

File tree

3 files changed

+339
-0
lines changed

3 files changed

+339
-0
lines changed

SB2_FileParser/Program.cs

Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections.Generic;
4+
using Newtonsoft.Json;
5+
using System.ComponentModel.DataAnnotations;
6+
using System.Reflection.Emit;
7+
using System.Reflection.Metadata;
8+
using Newtonsoft.Json.Converters;
9+
10+
namespace DC2AP
11+
{
12+
static class Program
13+
{
14+
static void Main(string[] args)
15+
{
16+
Console.WriteLine("SB2 File Parser");
17+
Console.WriteLine("Written by ArsonAssassin");
18+
var sb2Path = "";
19+
if (args.Length > 0)
20+
{
21+
sb2Path = args[0];
22+
}
23+
else
24+
{
25+
Console.WriteLine("Please input SB2 file path");
26+
sb2Path = Console.ReadLine();
27+
}
28+
SB2 fullFile = new SB2();
29+
using (var fileStream = new FileStream(sb2Path, FileMode.Open, FileAccess.Read))
30+
using (var reader = new BinaryReader(fileStream))
31+
{
32+
//Parse Header
33+
fullFile.Header = new SB2Header();
34+
var magic = new string(reader.ReadChars(4));
35+
if (magic != "SB2\0")
36+
throw new InvalidDataException("Invalid magic number in header.");
37+
fullFile.Header.Magic = magic;
38+
fullFile.Header.unk_function_data_offset = ReadUint(reader);
39+
fullFile.Header.codeStartByteOffset = ReadUint(reader);
40+
fullFile.Header.header_byte_count = ReadUint(reader);
41+
fullFile.Header.external_function_count = ReadUint(reader);
42+
fullFile.Header.unk0 = ReadUint(reader);
43+
fullFile.Header.global_variable_count = ReadUint(reader);
44+
45+
fullFile.Header.unk1 = ReadUint(reader);
46+
fullFile.Header.unk2 = ReadUint(reader);
47+
fullFile.Header.unk3 = ReadUint(reader);
48+
fullFile.Header.unk4 = ReadUint(reader);
49+
fullFile.Header.unk5 = ReadUint(reader);
50+
fullFile.Header.unk6 = ReadUint(reader);
51+
fullFile.Header.unk7 = ReadUint(reader);
52+
fullFile.Header.unk8 = ReadUint(reader);
53+
fullFile.Header.unk9 = ReadUint(reader);
54+
55+
Console.WriteLine("Header Parsed successfully");
56+
Console.WriteLine(JsonConvert.SerializeObject(fullFile.Header, Formatting.Indented));
57+
fullFile.ExternalFunctionTable = new List<SB2ExternalFunctionTableEntry>();
58+
for (int i = 0; i < fullFile.Header.external_function_count; i++)
59+
{
60+
SB2ExternalFunctionTableEntry sB2ExternalFunctionTableEntry = new SB2ExternalFunctionTableEntry();
61+
sB2ExternalFunctionTableEntry.Id = ReadUint(reader);
62+
sB2ExternalFunctionTableEntry.function_data_byte_offset = ReadUint(reader);
63+
fullFile.ExternalFunctionTable.Add(sB2ExternalFunctionTableEntry);
64+
}
65+
Console.WriteLine("External Function Table Parsed successfully");
66+
Console.WriteLine(JsonConvert.SerializeObject(fullFile.ExternalFunctionTable, Formatting.Indented));
67+
fullFile.Functions = new List<SB2Function>();
68+
for (int i = 0; i < fullFile.ExternalFunctionTable.Count; i++)
69+
{
70+
var functionEntry = fullFile.ExternalFunctionTable[i];
71+
SB2Function func = new SB2Function();
72+
func.Instructions = new List<Sb2Instruction>();
73+
reader.BaseStream.Seek(functionEntry.function_data_byte_offset, SeekOrigin.Begin);
74+
func.CodeStartOffset = ReadUint(reader);
75+
func.NameByteOffset = ReadUint(reader);
76+
func.StackSize = ReadUint(reader);
77+
func.ArgumentCount = ReadUint(reader);
78+
func.unk0 = ReadUint(reader);
79+
func.unk1 = ReadUint(reader);
80+
func.unk2 = ReadUint(reader);
81+
func.unk3 = ReadUint(reader);
82+
func.unk4 = ReadUint(reader);
83+
func.unk5 = ReadUint(reader);
84+
func.unk6 = ReadUint(reader);
85+
func.unk7 = ReadUint(reader);
86+
func.unk8 = ReadUint(reader);
87+
func.unk9 = ReadUint(reader);
88+
89+
reader.BaseStream.Seek(func.CodeStartOffset, SeekOrigin.Begin);
90+
OpCode currentOpCode = OpCode.count;
91+
while (!(currentOpCode == OpCode._end))
92+
{
93+
var instruction = ReadInstruction(reader);
94+
func.Instructions.Add(instruction);
95+
currentOpCode = instruction.OpCode;
96+
}
97+
if(fullFile.ExternalFunctionTable.Last().Id != functionEntry.Id)
98+
{
99+
var nextFuncStart = fullFile.ExternalFunctionTable[i + 1].function_data_byte_offset;
100+
var currentLocation = reader.BaseStream.Position;
101+
var currentDataSize = nextFuncStart - reader.BaseStream.Position;
102+
byte[] localData = new byte[currentDataSize];
103+
reader.BaseStream.Read(localData, 0, localData.Length);
104+
func.LocalData = localData;
105+
}
106+
else
107+
{
108+
var currentLocation = reader.BaseStream.Position;
109+
var currentDataSize = reader.BaseStream.Length - reader.BaseStream.Position;
110+
byte[] localData = new byte[currentDataSize];
111+
reader.BaseStream.Read(localData, 0, localData.Length);
112+
func.LocalData = localData;
113+
}
114+
115+
fullFile.Functions.Add(func);
116+
Console.WriteLine("Function read successfully");
117+
Console.WriteLine(JsonConvert.SerializeObject(func, Formatting.Indented));
118+
}
119+
Console.WriteLine("Function list extract complete");
120+
}
121+
122+
Console.WriteLine("File Parsed Successfully.");
123+
var json = JsonConvert.SerializeObject(fullFile, Formatting.Indented);
124+
var jsonPath = sb2Path.Replace(".stb", ".json");
125+
File.WriteAllText(jsonPath, json);
126+
}
127+
128+
private static uint ReadUint(BinaryReader reader)
129+
{
130+
var bytes = reader.ReadBytes(4);
131+
return BitConverter.ToUInt32(bytes, 0);
132+
}
133+
private static Sb2Instruction ReadInstruction(BinaryReader reader)
134+
{
135+
var instruction = new Sb2Instruction();
136+
var instructionBits = ReadUint(reader);
137+
instruction.OpCode = (OpCode)(instructionBits & 0xFF);
138+
139+
switch (instruction.OpCode)
140+
{
141+
case OpCode._push_stack:
142+
case OpCode._push_ptr:
143+
instruction.Mode = (instructionBits >> 24) & 0xFF;
144+
instruction.Address = (instructionBits >> 8) & 0xFFFF;
145+
break;
146+
case OpCode._push:
147+
instruction.Value = (instructionBits >> 12) & 0xFFF;
148+
instruction.Type = (DataType)((instructionBits >> 8) & 0xF);
149+
break;
150+
case OpCode._cmp:
151+
instruction.Function = (ComparisonFunction)((instructionBits >> 8) & 0xF);
152+
break;
153+
case OpCode._jmp:
154+
instruction.Address = (instructionBits >> 8) & 0xFFFFFF;
155+
break;
156+
case OpCode._bf:
157+
case OpCode._bt:
158+
instruction.Address = (instructionBits >> 8) & 0xFFFFFF;
159+
instruction.Restore = (instructionBits >> 24) & 0xFF;
160+
break;
161+
case OpCode._div:
162+
case OpCode._mod:
163+
case OpCode._neg:
164+
case OpCode._itof:
165+
case OpCode._ftoi:
166+
case OpCode._deref:
167+
case OpCode._pop:
168+
case OpCode._add:
169+
case OpCode._sub:
170+
case OpCode._mul:
171+
// These opcodes follow the Empty encoding
172+
// No additional fields to extract
173+
break;
174+
default:
175+
break;
176+
}
177+
return instruction;
178+
}
179+
}
180+
181+
public class SB2
182+
{
183+
public SB2Header Header { get; set; }
184+
public List<SB2ExternalFunctionTableEntry> ExternalFunctionTable { get; set; }
185+
public List<SB2Function> Functions { get; set; }
186+
187+
188+
}
189+
public class SB2Header()
190+
{
191+
public string Magic { get; set; }
192+
public uint unk_function_data_offset { get; set; }
193+
public uint codeStartByteOffset { get; set; }
194+
public uint header_byte_count { get; set; }
195+
public uint external_function_count { get; set; }
196+
public uint unk0 { get; set; }
197+
public uint global_variable_count { get; set; }
198+
public uint unk1 { get; set; }
199+
public uint unk2 { get; set; }
200+
public uint unk3 { get; set; }
201+
public uint unk4 { get; set; }
202+
public uint unk5 { get; set; }
203+
public uint unk6 { get; set; }
204+
public uint unk7 { get; set; }
205+
public uint unk8 { get; set; }
206+
public uint unk9 { get; set; }
207+
}
208+
public class SB2ExternalFunctionTableEntry
209+
{
210+
public uint Id { get; set; }
211+
public uint function_data_byte_offset { get; set; }
212+
}
213+
214+
public class SB2Function()
215+
{
216+
public uint CodeStartOffset { get; set; }
217+
public uint NameByteOffset { get; set; }
218+
public uint StackSize { get; set; }
219+
public uint ArgumentCount { get; set; }
220+
public uint unk0 { get; set; }
221+
public uint unk1 { get; set; }
222+
public uint unk2 { get; set; }
223+
public uint unk3 { get; set; }
224+
public uint unk4 { get; set; }
225+
public uint unk5 { get; set; }
226+
public uint unk6 { get; set; }
227+
public uint unk7 { get; set; }
228+
public uint unk8 { get; set; }
229+
public uint unk9 { get; set; }
230+
public List<Sb2Instruction> Instructions { get; set; }
231+
public byte[] LocalData { get; set; }
232+
}
233+
public class Sb2Instruction
234+
{
235+
[JsonConverter(typeof(StringEnumConverter))]
236+
public OpCode OpCode { get; set; }
237+
public uint Mode { get; set; }
238+
public uint Address { get; set; }
239+
public uint Value { get; set; }
240+
[JsonConverter(typeof(StringEnumConverter))]
241+
public DataType Type { get; set; }
242+
public uint Restore { get; set; }
243+
[JsonConverter(typeof(StringEnumConverter))]
244+
public ComparisonFunction Function { get; set; }
245+
}
246+
public enum ComparisonFunction
247+
{
248+
_eq = 0,
249+
_ne = 1,
250+
_lt = 2,
251+
_le = 3,
252+
_gt = 4,
253+
_ge = 5,
254+
count
255+
}
256+
public enum DataType
257+
{
258+
invalid,
259+
_int = 1,
260+
_flt = 2,
261+
_str = 3,
262+
_ptr = 4,
263+
count
264+
}
265+
public enum OpCode
266+
{
267+
_end = 0,
268+
_push_stack = 1,
269+
_push_ptr = 2,
270+
_push = 3,
271+
_pop = 4,
272+
_deref = 5,
273+
_add = 6,
274+
_sub = 7,
275+
_mul = 8,
276+
_div = 9,
277+
_mod = 10,
278+
_neg = 11,
279+
_itof = 12,
280+
_ftoi = 13,
281+
_cmp = 14,
282+
_ret = 15,
283+
_jmp = 16,
284+
_bf = 17,
285+
_bt = 18,
286+
_call = 19,
287+
_print = 20,
288+
_ext = 21,
289+
_nop = 22,
290+
_yld = 23,
291+
_and = 24,
292+
_or = 25,
293+
_not = 26,
294+
_exit = 27,
295+
_unk1 = 28,
296+
_sin = 29,
297+
_cos = 30,
298+
count
299+
}
300+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
12+
</ItemGroup>
13+
14+
</Project>

SB2_FileParser/SB2_FileParser.sln

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
Microsoft Visual Studio Solution File, Format Version 12.00
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.9.34701.34
5+
MinimumVisualStudioVersion = 10.0.40219.1
6+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SB2_FileParser", "SB2_FileParser.csproj", "{1E23E910-3CDF-4CDE-B525-620FBB7EE448}"
7+
EndProject
8+
Global
9+
GlobalSection(SolutionConfigurationPlatforms) = preSolution
10+
Debug|Any CPU = Debug|Any CPU
11+
Release|Any CPU = Release|Any CPU
12+
EndGlobalSection
13+
GlobalSection(ProjectConfigurationPlatforms) = postSolution
14+
{1E23E910-3CDF-4CDE-B525-620FBB7EE448}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15+
{1E23E910-3CDF-4CDE-B525-620FBB7EE448}.Debug|Any CPU.Build.0 = Debug|Any CPU
16+
{1E23E910-3CDF-4CDE-B525-620FBB7EE448}.Release|Any CPU.ActiveCfg = Release|Any CPU
17+
{1E23E910-3CDF-4CDE-B525-620FBB7EE448}.Release|Any CPU.Build.0 = Release|Any CPU
18+
EndGlobalSection
19+
GlobalSection(SolutionProperties) = preSolution
20+
HideSolutionNode = FALSE
21+
EndGlobalSection
22+
GlobalSection(ExtensibilityGlobals) = postSolution
23+
SolutionGuid = {9F3E8EC5-5CCE-4D97-9753-F8CCF49EA98D}
24+
EndGlobalSection
25+
EndGlobal

0 commit comments

Comments
 (0)