mirror of
https://github.com/lifegpc/GARbro.git
synced 2026-07-08 01:31:41 +08:00
implemented another version of MRG archives.
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
//! \date Mon Jul 13 03:20:13 2015
|
//! \date Mon Jul 13 03:20:13 2015
|
||||||
//! \brief F&C Co. MGR archive format.
|
//! \brief F&C Co. MGR archive format.
|
||||||
//
|
//
|
||||||
// Copyright (C) 2015 by morkt
|
// Copyright (C) 2015-2016 by morkt
|
||||||
//
|
//
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
// of this software and associated documentation files (the "Software"), to
|
// of this software and associated documentation files (the "Software"), to
|
||||||
@@ -63,15 +63,17 @@ namespace GameRes.Formats.FC01
|
|||||||
uint index_size = file.View.ReadUInt32 (8) - 0x10;
|
uint index_size = file.View.ReadUInt32 (8) - 0x10;
|
||||||
if (index_size < 0x40 || index_size >= file.MaxOffset)
|
if (index_size < 0x40 || index_size >= file.MaxOffset)
|
||||||
return null;
|
return null;
|
||||||
var index = new byte[index_size];
|
var index = file.View.ReadBytes (0x10, index_size);
|
||||||
if (index.Length != file.View.Read (0x10, index, 0, index_size))
|
if (index.Length != index_size)
|
||||||
return null;
|
return null;
|
||||||
/*
|
/*
|
||||||
var key_src = KnownKey;
|
var key_src = KnownKey;
|
||||||
if (key1index >= key_src.Item1.Length || key2index >= key_src.Item2.Length)
|
if (key1index >= key_src.Item1.Length || key2index >= key_src.Item2.Length)
|
||||||
return null;
|
return null;
|
||||||
byte index_key = (byte)(key_src.Item1[key1index] + key_src.Item2[key2index]);
|
byte? key = (byte)(key_src.Item1[key1index] + key_src.Item2[key2index]);
|
||||||
*/
|
*/
|
||||||
|
if (key2index >= 2)
|
||||||
|
return null;
|
||||||
var key = GuessKey (file, index);
|
var key = GuessKey (file, index);
|
||||||
if (null == key)
|
if (null == key)
|
||||||
throw new UnknownEncryptionScheme();
|
throw new UnknownEncryptionScheme();
|
||||||
@@ -83,13 +85,10 @@ namespace GameRes.Formats.FC01
|
|||||||
for (int i = 0; i < count; ++i)
|
for (int i = 0; i < count; ++i)
|
||||||
{
|
{
|
||||||
string name = Binary.GetCString (index, current_offset, 0x0E);
|
string name = Binary.GetCString (index, current_offset, 0x0E);
|
||||||
var entry = new MrgEntry
|
var entry = FormatCatalog.Instance.Create<MrgEntry> (name);
|
||||||
{
|
entry.Offset = next_offset;
|
||||||
Name = name,
|
entry.Method = index[current_offset+0x12];
|
||||||
Type = FormatCatalog.Instance.GetTypeFromName (name),
|
|
||||||
Offset = next_offset,
|
|
||||||
Method = index[current_offset+0x12],
|
|
||||||
};
|
|
||||||
next_offset = LittleEndian.ToUInt32 (index, current_offset+0x3C);
|
next_offset = LittleEndian.ToUInt32 (index, current_offset+0x3C);
|
||||||
entry.Size = next_offset - (uint)entry.Offset;
|
entry.Size = next_offset - (uint)entry.Offset;
|
||||||
if (entry.Offset < index_size || !entry.CheckPlacement (file.MaxOffset))
|
if (entry.Offset < index_size || !entry.CheckPlacement (file.MaxOffset))
|
||||||
@@ -112,8 +111,7 @@ namespace GameRes.Formats.FC01
|
|||||||
{
|
{
|
||||||
if (entry.Size < 0x108)
|
if (entry.Size < 0x108)
|
||||||
return arc.File.CreateStream (entry.Offset, entry.Size);
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
var data = new byte[entry.Size];
|
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||||
arc.File.View.Read (entry.Offset, data, 0, entry.Size);
|
|
||||||
var reader = new MrgDecoder (data);
|
var reader = new MrgDecoder (data);
|
||||||
reader.Unpack();
|
reader.Unpack();
|
||||||
input = new MemoryStream (reader.Data);
|
input = new MemoryStream (reader.Data);
|
||||||
@@ -169,6 +167,132 @@ namespace GameRes.Formats.FC01
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal class Mrg2Entry : MrgEntry
|
||||||
|
{
|
||||||
|
public uint ArcKey;
|
||||||
|
public uint Key;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Export(typeof(ArchiveFormat))]
|
||||||
|
public class Mrg2Opener : ArchiveFormat
|
||||||
|
{
|
||||||
|
public override string Tag { get { return "MRG/2"; } }
|
||||||
|
public override string Description { get { return "Overture engine resource archive"; } }
|
||||||
|
public override uint Signature { get { return 0x0047524D; } } // 'MRG'
|
||||||
|
public override bool IsHierarchic { get { return false; } }
|
||||||
|
public override bool CanCreate { get { return false; } }
|
||||||
|
|
||||||
|
public Mrg2Opener ()
|
||||||
|
{
|
||||||
|
Extensions = new string[] { "mrg" };
|
||||||
|
}
|
||||||
|
|
||||||
|
public override ArcFile TryOpen (ArcView file)
|
||||||
|
{
|
||||||
|
int count = file.View.ReadInt32 (12);
|
||||||
|
if (!IsSaneCount (count))
|
||||||
|
return null;
|
||||||
|
int version = file.View.ReadUInt16 (6);
|
||||||
|
if (version < 2)
|
||||||
|
return null;
|
||||||
|
uint index_size = file.View.ReadUInt32 (8) - 0x10;
|
||||||
|
if (index_size < 0x40 || index_size >= file.MaxOffset)
|
||||||
|
return null;
|
||||||
|
var index = file.View.ReadBytes (0x10, index_size);
|
||||||
|
if (index.Length != index_size)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
uint arc_checksum = GetNameChecksum (Path.GetFileName (file.Name));
|
||||||
|
Decrypt (index, 0, index.Length, arc_checksum, 0x285EE76F);
|
||||||
|
int current_offset = 0;
|
||||||
|
uint next_offset = LittleEndian.ToUInt32 (index, current_offset+0x4F);
|
||||||
|
var dir = new List<Entry> (count);
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
string name = Binary.GetCString (index, current_offset, 0x40);
|
||||||
|
var entry = FormatCatalog.Instance.Create<Mrg2Entry> (name);
|
||||||
|
entry.Offset = next_offset;
|
||||||
|
next_offset = LittleEndian.ToUInt32 (index, current_offset+0xA6);
|
||||||
|
entry.Size = next_offset - (uint)entry.Offset;
|
||||||
|
if (!entry.CheckPlacement (file.MaxOffset))
|
||||||
|
return null;
|
||||||
|
entry.Method = LittleEndian.ToUInt16 (index, current_offset+0x45);
|
||||||
|
if (0 == entry.Method)
|
||||||
|
{
|
||||||
|
entry.ArcKey = arc_checksum;
|
||||||
|
entry.Key = GetNameChecksum (entry.Name);
|
||||||
|
}
|
||||||
|
entry.IsPacked = entry.Method != 0;
|
||||||
|
entry.UnpackedSize = LittleEndian.ToUInt32 (index, current_offset+0x41);
|
||||||
|
dir.Add (entry);
|
||||||
|
current_offset += 0x57;
|
||||||
|
}
|
||||||
|
return new ArcFile (file, this, dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Stream OpenEntry (ArcFile arc, Entry entry)
|
||||||
|
{
|
||||||
|
var mrg_entry = entry as Mrg2Entry;
|
||||||
|
if (null == mrg_entry || mrg_entry.Method > 3)
|
||||||
|
return arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
Stream input;
|
||||||
|
if (0 == mrg_entry.Method)
|
||||||
|
{
|
||||||
|
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||||
|
Decrypt (data, 0, data.Length, mrg_entry.Key, mrg_entry.ArcKey);
|
||||||
|
input = new MemoryStream (data);
|
||||||
|
}
|
||||||
|
else if (mrg_entry.Method >= 2)
|
||||||
|
{
|
||||||
|
var data = arc.File.View.ReadBytes (entry.Offset, entry.Size);
|
||||||
|
var reader = new MrgDecoder (data);
|
||||||
|
reader.Unpack();
|
||||||
|
input = new MemoryStream (reader.Data);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
input = arc.File.CreateStream (entry.Offset, entry.Size);
|
||||||
|
if (1 == mrg_entry.Method || 3 == mrg_entry.Method)
|
||||||
|
{
|
||||||
|
using (input)
|
||||||
|
using (var reader = new MrgLzssReader (input, (int)input.Length, (int)mrg_entry.UnpackedSize))
|
||||||
|
{
|
||||||
|
reader.Unpack();
|
||||||
|
return new MemoryStream (reader.Data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return input;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Decrypt (byte[] data, int index, int length, uint checksum, uint key)
|
||||||
|
{
|
||||||
|
var table = new byte[0x100];
|
||||||
|
for (int i = 0; i < 0x100; ++i)
|
||||||
|
{
|
||||||
|
uint n = key + Binary.RotL (checksum, 16);
|
||||||
|
key = checksum;
|
||||||
|
checksum += n;
|
||||||
|
table[i] = (byte)checksum;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
{
|
||||||
|
data[index+i] ^= table[i & 0xFF];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint GetNameChecksum (string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty (name))
|
||||||
|
return 0;
|
||||||
|
uint checksum = char.ToUpper (name[0]);
|
||||||
|
for (int i = 0; i < name.Length; ++i)
|
||||||
|
{
|
||||||
|
if (name[i] != '.')
|
||||||
|
checksum += char.ToUpper (name[i]) + (checksum << 6);
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// LZSS decompression with slightly modified offset/count values encoding.
|
/// LZSS decompression with slightly modified offset/count values encoding.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -182,7 +306,7 @@ namespace GameRes.Formats.FC01
|
|||||||
|
|
||||||
public MrgLzssReader (Stream input, int input_length, int output_length)
|
public MrgLzssReader (Stream input, int input_length, int output_length)
|
||||||
{
|
{
|
||||||
m_input = new BinaryReader (input, Encoding.ASCII, true);
|
m_input = new ArcView.Reader (input);
|
||||||
m_output = new byte[output_length];
|
m_output = new byte[output_length];
|
||||||
m_size = input_length;
|
m_size = input_length;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -567,6 +567,7 @@ Yuukyou Gangu 2<br/>
|
|||||||
Asa no Konai Yoru ni Dakarete -Eternal Night-<br/>
|
Asa no Konai Yoru ni Dakarete -Eternal Night-<br/>
|
||||||
Guren ni Somaru Gin no Rozario<br/>
|
Guren ni Somaru Gin no Rozario<br/>
|
||||||
Konata yori Kanata made<br/>
|
Konata yori Kanata made<br/>
|
||||||
|
Thirua Panic<br/>
|
||||||
</td></tr>
|
</td></tr>
|
||||||
<tr><td>*.mcg</td><td><tt>MCG 2.00</tt></td><td>No</td></tr>
|
<tr><td>*.mcg</td><td><tt>MCG 2.00</tt></td><td>No</td></tr>
|
||||||
<tr><td>*.acd</td><td><tt>ACD 1.00</tt></td><td>No</td></tr>
|
<tr><td>*.acd</td><td><tt>ACD 1.00</tt></td><td>No</td></tr>
|
||||||
|
|||||||
Reference in New Issue
Block a user