feat: support new index format of Cyberworks/TinkerBell games after 2024

This commit is contained in:
scientificworld
2026-01-16 12:33:33 +08:00
parent d37285960f
commit 7355ead1f6

View File

@@ -220,7 +220,7 @@ namespace GameRes.Formats.Cyberworks
var toc = ReadToc (toc_name, 8); var toc = ReadToc (toc_name, 8);
if (null == toc) if (null == toc)
return null; return null;
using (var index = new ArcIndexReader (toc, file, arc_idx, game_name)) using (var index = GetIndexReader (toc, file, arc_idx, game_name))
{ {
if (!index.Read()) if (!index.Read())
return null; return null;
@@ -228,6 +228,16 @@ namespace GameRes.Formats.Cyberworks
} }
} }
internal virtual IndexReader GetIndexReader (byte[] toc, ArcView file, int arc_idx, string game_name)
{
return new ArcIndexReader (toc, file, arc_idx, game_name);
}
internal virtual TocUnpacker GetTocUnpacker (string toc_name)
{
return new TocUnpacker (toc_name);
}
internal ArcFile ArchiveFromDir (ArcView file, List<Entry> dir, bool has_images) internal ArcFile ArchiveFromDir (ArcView file, List<Entry> dir, bool has_images)
{ {
if (0 == dir.Count) if (0 == dir.Count)
@@ -245,7 +255,7 @@ namespace GameRes.Formats.Cyberworks
{ {
if (!VFS.FileExists (meta_arc_name)) if (!VFS.FileExists (meta_arc_name))
return null; return null;
using (var unpacker = new TocUnpacker (meta_arc_name)) using (var unpacker = GetTocUnpacker (meta_arc_name))
{ {
if (unpacker.Length > 0x1000) if (unpacker.Length > 0x1000)
return null; return null;
@@ -269,7 +279,7 @@ namespace GameRes.Formats.Cyberworks
{ {
if (!VFS.FileExists (toc_name)) if (!VFS.FileExists (toc_name))
return null; return null;
using (var toc_unpacker = new TocUnpacker (toc_name)) using (var toc_unpacker = GetTocUnpacker (toc_name))
return toc_unpacker.Unpack (num_length); return toc_unpacker.Unpack (num_length);
} }
@@ -403,6 +413,26 @@ namespace GameRes.Formats.Cyberworks
public AImageScheme Scheme; public AImageScheme Scheme;
} }
[Export(typeof(ArchiveFormat))]
public class DatOpener2024 : DatOpener
{
public override string Tag { get { return "ARC/Cyberworks/2"; } }
public override string Description { get { return "Cyberworks/TinkerBell resource archive"; } }
public override uint Signature { get { return 0; } }
public override bool IsHierarchic { get { return false; } }
public override bool CanWrite { get { return false; } }
internal override IndexReader GetIndexReader (byte[] toc, ArcView file, int arc_idx, string game_name)
{
return new ArcIndexReader2 (toc, file, arc_idx, game_name);
}
internal override TocUnpacker GetTocUnpacker (string toc_name)
{
return new TocUnpacker (toc_name, true);
}
}
[Export(typeof(ArchiveFormat))] [Export(typeof(ArchiveFormat))]
public class OldDatOpener : DatOpener public class OldDatOpener : DatOpener
{ {
@@ -514,19 +544,22 @@ namespace GameRes.Formats.Cyberworks
{ {
ArcView m_file; ArcView m_file;
bool m_should_dispose; bool m_should_dispose;
bool m_reversed_decimal;
public long Length { get { return m_file.MaxOffset; } } public long Length { get { return m_file.MaxOffset; } }
public uint PackedSize { get; private set; } public uint PackedSize { get; private set; }
public uint UnpackedSize { get; private set; } public uint UnpackedSize { get; private set; }
public TocUnpacker (string toc_name) : this (VFS.OpenView (toc_name), true) public TocUnpacker (string toc_name, bool reversed_decimal = false)
: this (VFS.OpenView (toc_name), true, reversed_decimal)
{ {
} }
public TocUnpacker (ArcView file, bool should_dispose = false) public TocUnpacker (ArcView file, bool should_dispose = false, bool reversed_decimal = false)
{ {
m_file = file; m_file = file;
m_should_dispose = should_dispose; m_should_dispose = should_dispose;
m_reversed_decimal = reversed_decimal;
} }
public byte[] Unpack (int num_length) public byte[] Unpack (int num_length)
@@ -564,7 +597,22 @@ namespace GameRes.Formats.Cyberworks
{ {
uint v = 0; uint v = 0;
uint rank = 1; uint rank = 1;
for (int i = num_length-1; i >= 0; --i, rank *= 10)
int start, end, step;
if (m_reversed_decimal)
{
start = 0;
end = num_length;
step = 1;
}
else
{
start = num_length-1;
end = -1;
step = -1;
}
for (int i = start; i != end; i += step, rank *= 10)
{ {
uint b = m_file.View.ReadByte (offset+i); uint b = m_file.View.ReadByte (offset+i);
if (b != 0xFF) if (b != 0xFF)
@@ -627,9 +675,9 @@ namespace GameRes.Formats.Cyberworks
return true; return true;
} }
uint m_fault_id = 100000; protected uint m_fault_id = 100000;
internal PackedEntry ReadEntryInfo () internal virtual PackedEntry ReadEntryInfo ()
{ {
uint id = m_index.ReadUInt32(); uint id = m_index.ReadUInt32();
if (id > m_fault_id) if (id > m_fault_id)
@@ -707,6 +755,27 @@ namespace GameRes.Formats.Cyberworks
} }
} }
internal class ArcIndexReader2 : ArcIndexReader
{
public ArcIndexReader2 (byte[] toc, ArcView file, int arc_number, string game_name = null)
: base (toc, file, arc_number, game_name)
{
}
internal override PackedEntry ReadEntryInfo ()
{
uint id = m_index.ReadUInt32();
if (id > m_fault_id)
id = m_fault_id++;
var entry = new PackedEntry { Name = id.ToString ("D6") };
entry.UnpackedSize = m_index.ReadUInt32();
entry.Offset = m_index.ReadUInt32();
entry.Size = m_index.ReadUInt32();
entry.IsPacked = entry.UnpackedSize != entry.Size && entry.UnpackedSize != 0;
return entry;
}
}
internal class DatIndexReader : IndexReader internal class DatIndexReader : IndexReader
{ {
public DatIndexReader (byte[] toc, ArcView file) : base (toc, file) public DatIndexReader (byte[] toc, ArcView file) : base (toc, file)