@MarltoroHi !!!
Thanks for sharing the code, it's been almost a month I've tried to decipher those DPC files and always hit a wall ...
I guess you've used IDA because the code really looks like what I've seen with it ?
Anyway here's my modest contribution, hopefully things can advance a bit forward: a much simpler DPC parser (hopefully).
Haven't looked in details at your code, tried to step through it a bit but was stumped by the complexity (actually it's just like IDA minus the environment), wondered if there was a much simpler way and it looks like there is (i.e. just tried to understand those pointers and went from there).
So all TM2 files are successfully read, I get I a pretty close model count to yours (there seems to be very small or irrelevant ones my code didn't consider):
Code:E:\tmp\TM2PC\CARSDB\test\AX.DPC 29 // file + models found in it
E:\tmp\TM2PC\CARSDB\test\BB.DPC 115
E:\tmp\TM2PC\CARSDB\test\FL.DPC 23
E:\tmp\TM2PC\CARSDB\test\HH.DPC 17
E:\tmp\TM2PC\CARSDB\test\HS.DPC 22
E:\tmp\TM2PC\CARSDB\test\IR.DPC 21
E:\tmp\TM2PC\CARSDB\test\MG.DPC 14
E:\tmp\TM2PC\CARSDB\test\MN.DPC 22
E:\tmp\TM2PC\CARSDB\test\OT.DPC 55
E:\tmp\TM2PC\CARSDB\test\PV.DPC 20
E:\tmp\TM2PC\CARSDB\test\RK.DPC 30
E:\tmp\TM2PC\CARSDB\test\SP.DPC 35
E:\tmp\TM2PC\CARSDB\test\ST.DPC 23
E:\tmp\TM2PC\CARSDB\test\TH.DPC 21
Here's the source:
Code:using System;
using System.IO;
using JetBrains.Annotations;
namespace ConsoleApplication1
{
[PublicAPI]
public struct Pointer
{
public uint Address { get; }
public Pointer(uint address)
{
Address = address;
}
public Pointer(BinaryReader reader)
{
if (reader == null)
throw new ArgumentNullException(nameof(reader));
Address = reader.ReadUInt32();
}
public static explicit operator int(Pointer pointer)
{
return (int)pointer.Address;
}
public static Pointer operator -(Pointer a, Pointer b)
{
return new Pointer(a.Address - b.Address);
}
public override string ToString()
{
return $"0x{Address:X8} ({Address})";
}
}
public static class Extensions
{
public static void Offset(this BinaryReader reader, int count)
{
reader.BaseStream.Position += count;
}
public static void Position(this BinaryReader reader, int position)
{
reader.BaseStream.Position = position;
}
public static uint PeekUInt32(this BinaryReader reader)
{
var u = reader.ReadUInt32();
reader.Offset(-4);
return u;
}
public static Pointer ReadPointer(this BinaryReader reader)
{
return new Pointer(reader);
}
}
internal static class Program
{
private static void Main(string[] args)
{
//var tpc = @"E:\tmp\TM2PC\CARSDB\WH.TPC";
//var dpc = @"E:\tmp\TM2PC\CARSDB\WH.DPC";
//var tpcFile = new TpcFile();
//var tpcFileController = new TpcFileController(tpcFile);
//tpcFileController.ReadFile(tpc);
//var dpcFile = new DpcFile();
//var dpcFileController = new DpcFileController(dpcFile);
//dpcFileController.ReadFile(dpc, tpcFile.TimFiles);
var files = Directory.GetFiles(@"E:\tmp\TM2PC\CARSDB\test", "*.dpc");
foreach (var file in files)
GetValue(file);
}
private static void GetValue(string path)
{
using (var stream = File.OpenRead(path))
{
var reader = new BinaryReader(stream);
// header
var magic = reader.ReadInt64();
if (magic != 0x000000434D504344)
throw new InvalidDataException();
var date = new DateTime(1970, 1, 1).AddSeconds(reader.ReadUInt32());
var ptr1 = reader.ReadPointer();
// vertices
reader.Offset(24);
while (reader.PeekUInt32() != 0x0000FF00)
reader.ReadBytes(8);
reader.ReadUInt32();
// models
var modelCount = 0;
while (stream.Position < stream.Length)
{
while (reader.ReadUInt32() != 0x0000FF00)
if (stream.Position == stream.Length)
goto end; // no reliable way yet
reader.Offset(8);
var ptr2 = reader.ReadPointer();
var start = ptr2 - ptr1;
var count = reader.ReadUInt32();
reader.Position((int)start);
for (var i = 0; i < count; i++)
{
var u = reader.PeekUInt32();
switch (u)
{
case 0x05070100:
reader.Offset(28);
break;
case 0x04080100:
case 0x05080100:
reader.Offset(32);
break;
case 0x08090100:
case 0x06090100:
case 0x06090300:
reader.Offset(36);
break;
case 0x060A0300:
reader.Offset(40);
break;
case 0x060B0300:
case 0x070B0103:
reader.Offset(44);
break;
case 0x070C0103:
case 0x070C0183:
case 0x090C0104:
reader.Offset(48);
break;
case 0x070D0183:
case 0x090D0103:
case 0x090D0104:
case 0x090D0184:
case 0x090D0303:
reader.Offset(52);
break;
case 0x0C0E0104:
case 0x090E0183:
case 0x090E0184:
case 0x090E0303:
case 0x090E0383:
reader.Offset(56);
break;
case 0x0C0F0184:
case 0x090F0303:
case 0x090F0383:
reader.Offset(60);
break;
case 0x09100383:
reader.Offset(64);
break;
default:
throw new NotImplementedException();
}
}
modelCount++;
}
end:
;
Console.WriteLine(path + "\t" + modelCount);
}
}
}
}
It looks like pretty much other PSX games, ~10 polygon types I guess 4 being triangles, 4 being quads and others for sprites (with flat/gouraud shading flavors or whatever, etc...).
(note: I've reverse-engineered Wipeout 1/2/3 for PSX in the past)
Obviously it's just a parser done in 2 hours and really needs a lot more work, but it achieves reading all TM2 DPCs.
Unfortunately it fails hard on TM1, obviously ... (my favorite game)
Waiting for your comments/remarks, cheers