mirror of
https://github.com/claunia/SabreTools.git
synced 2025-12-16 19:14:27 +00:00
[CHDFile] Add CHD parsing logic
This commit is contained in:
@@ -3705,7 +3705,7 @@ namespace SabreTools.Library.DatFiles
|
|||||||
bool addDate, string headerToCheckAgainst)
|
bool addDate, string headerToCheckAgainst)
|
||||||
{
|
{
|
||||||
Globals.Logger.Verbose("'{0}' treated like a file", Path.GetFileName(item));
|
Globals.Logger.Verbose("'{0}' treated like a file", Path.GetFileName(item));
|
||||||
Rom rom = FileTools.GetFileInfo(item, omitFromScan: omitFromScan, date: addDate, header: headerToCheckAgainst);
|
DatItem rom = FileTools.GetFileInfo(item, omitFromScan: omitFromScan, date: addDate, header: headerToCheckAgainst);
|
||||||
|
|
||||||
ProcessFileHelper(item, rom, basePath, parent);
|
ProcessFileHelper(item, rom, basePath, parent);
|
||||||
}
|
}
|
||||||
@@ -4211,7 +4211,7 @@ namespace SabreTools.Library.DatFiles
|
|||||||
if (shouldExternalProcess)
|
if (shouldExternalProcess)
|
||||||
{
|
{
|
||||||
// TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
// TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
||||||
Rom fileinfo = FileTools.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes), header: headerToCheckAgainst);
|
DatItem fileinfo = FileTools.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes), header: headerToCheckAgainst);
|
||||||
usedExternally = RebuildIndividualFile(fileinfo, file, outDir, tempSubDir, date, inverse, outputFormat,
|
usedExternally = RebuildIndividualFile(fileinfo, file, outDir, tempSubDir, date, inverse, outputFormat,
|
||||||
romba, updateDat, null /* isZip */, headerToCheckAgainst);
|
romba, updateDat, null /* isZip */, headerToCheckAgainst);
|
||||||
}
|
}
|
||||||
@@ -4242,7 +4242,7 @@ namespace SabreTools.Library.DatFiles
|
|||||||
if (entries == null && File.Exists(file))
|
if (entries == null && File.Exists(file))
|
||||||
{
|
{
|
||||||
// TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
// TODO: All instances of Hash.DeepHashes should be made into 0x0 eventually
|
||||||
Rom fileinfo = FileTools.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes));
|
DatItem fileinfo = FileTools.GetFileInfo(file, omitFromScan: (quickScan ? Hash.SecureHashes : Hash.DeepHashes));
|
||||||
usedExternally = RebuildIndividualFile(fileinfo, file, outDir, tempSubDir, date, inverse, outputFormat,
|
usedExternally = RebuildIndividualFile(fileinfo, file, outDir, tempSubDir, date, inverse, outputFormat,
|
||||||
romba, updateDat, null /* isZip */, headerToCheckAgainst);
|
romba, updateDat, null /* isZip */, headerToCheckAgainst);
|
||||||
}
|
}
|
||||||
@@ -4454,7 +4454,7 @@ namespace SabreTools.Library.DatFiles
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get the item from the current file
|
// Get the item from the current file
|
||||||
Rom item = FileTools.GetStreamInfo(fileStream, fileStream.Length, keepReadOpen: true);
|
Rom item = (Rom)FileTools.GetStreamInfo(fileStream, fileStream.Length, keepReadOpen: true);
|
||||||
item.MachineName = Style.GetFileNameWithoutExtension(item.Name);
|
item.MachineName = Style.GetFileNameWithoutExtension(item.Name);
|
||||||
item.MachineDescription = Style.GetFileNameWithoutExtension(item.Name);
|
item.MachineDescription = Style.GetFileNameWithoutExtension(item.Name);
|
||||||
|
|
||||||
@@ -4565,7 +4565,7 @@ namespace SabreTools.Library.DatFiles
|
|||||||
if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true))
|
if (rule.TransformStream(fileStream, transformStream, keepReadOpen: true, keepWriteOpen: true))
|
||||||
{
|
{
|
||||||
// Get the file informations that we will be using
|
// Get the file informations that we will be using
|
||||||
Rom headerless = FileTools.GetStreamInfo(transformStream, transformStream.Length, keepReadOpen: true);
|
Rom headerless = (Rom)FileTools.GetStreamInfo(transformStream, transformStream.Length, keepReadOpen: true);
|
||||||
|
|
||||||
// Find if the file has duplicates in the DAT
|
// Find if the file has duplicates in the DAT
|
||||||
hasDuplicates = headerless.HasDuplicates(this);
|
hasDuplicates = headerless.HasDuplicates(this);
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace SabreTools.Library.Data
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current toolset version to be used by all child applications
|
/// The current toolset version to be used by all child applications
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static string Version = "v0.9.8.1-" + Assembly.GetExecutingAssembly().GetLinkerTime().ToString("yyyy-MM-dd HH:mm:ss");
|
public readonly static string Version = "v0.9.8.1-" + Assembly.GetExecutingAssembly().GetLinkerTime().ToString("yyyy-MM-dd HH:mm:ss");
|
||||||
public const int HeaderHeight = 3;
|
public const int HeaderHeight = 3;
|
||||||
|
|
||||||
#region 0-byte file constants
|
#region 0-byte file constants
|
||||||
@@ -31,26 +31,36 @@ namespace SabreTools.Library.Data
|
|||||||
#region Byte (1000-based) size comparisons
|
#region Byte (1000-based) size comparisons
|
||||||
|
|
||||||
public const long KiloByte = 1000;
|
public const long KiloByte = 1000;
|
||||||
public static long MegaByte = (long)Math.Pow(KiloByte, 2);
|
public readonly static long MegaByte = (long)Math.Pow(KiloByte, 2);
|
||||||
public static long GigaByte = (long)Math.Pow(KiloByte, 3);
|
public readonly static long GigaByte = (long)Math.Pow(KiloByte, 3);
|
||||||
public static long TeraByte = (long)Math.Pow(KiloByte, 4);
|
public readonly static long TeraByte = (long)Math.Pow(KiloByte, 4);
|
||||||
public static long PetaByte = (long)Math.Pow(KiloByte, 5);
|
public readonly static long PetaByte = (long)Math.Pow(KiloByte, 5);
|
||||||
public static long ExaByte = (long)Math.Pow(KiloByte, 6);
|
public readonly static long ExaByte = (long)Math.Pow(KiloByte, 6);
|
||||||
public static long ZettaByte = (long)Math.Pow(KiloByte, 7);
|
public readonly static long ZettaByte = (long)Math.Pow(KiloByte, 7);
|
||||||
public static long YottaByte = (long)Math.Pow(KiloByte, 8);
|
public readonly static long YottaByte = (long)Math.Pow(KiloByte, 8);
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Byte (1024-based) size comparisons
|
#region Byte (1024-based) size comparisons
|
||||||
|
|
||||||
public const long KibiByte = 1024;
|
public const long KibiByte = 1024;
|
||||||
public static long MibiByte = (long)Math.Pow(KibiByte, 2);
|
public readonly static long MibiByte = (long)Math.Pow(KibiByte, 2);
|
||||||
public static long GibiByte = (long)Math.Pow(KibiByte, 3);
|
public readonly static long GibiByte = (long)Math.Pow(KibiByte, 3);
|
||||||
public static long TibiByte = (long)Math.Pow(KibiByte, 4);
|
public readonly static long TibiByte = (long)Math.Pow(KibiByte, 4);
|
||||||
public static long PibiByte = (long)Math.Pow(KibiByte, 5);
|
public readonly static long PibiByte = (long)Math.Pow(KibiByte, 5);
|
||||||
public static long ExiByte = (long)Math.Pow(KibiByte, 6);
|
public readonly static long ExiByte = (long)Math.Pow(KibiByte, 6);
|
||||||
public static long ZittiByte = (long)Math.Pow(KibiByte, 7);
|
public readonly static long ZittiByte = (long)Math.Pow(KibiByte, 7);
|
||||||
public static long YittiByte = (long)Math.Pow(KibiByte, 8);
|
public readonly static long YittiByte = (long)Math.Pow(KibiByte, 8);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region CHD header values
|
||||||
|
|
||||||
|
public readonly static byte[] CHDSignature = { 0x4d, 0x43, 0x6f, 0x6d, 0x70, 0x72, 0x48, 0x44 }; // "MComprHD"
|
||||||
|
public const int CHD_HEADER_VERSION = 5;
|
||||||
|
public const int CHD_V3_HEADER_SIZE = 120;
|
||||||
|
public const int CHD_V4_HEADER_SIZE = 108;
|
||||||
|
public const int CHD_V5_HEADER_SIZE = 124;
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -109,7 +119,7 @@ namespace SabreTools.Library.Data
|
|||||||
0A-0B Last mod file time (0x00, 0xBC)
|
0A-0B Last mod file time (0x00, 0xBC)
|
||||||
0C-0D Last mod file date (0x98, 0x21)
|
0C-0D Last mod file date (0x98, 0x21)
|
||||||
*/
|
*/
|
||||||
public static byte[] TorrentZipHeader = new byte[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0xbc, 0x98, 0x21 };
|
public readonly static byte[] TorrentZipHeader = new byte[] { 0x50, 0x4b, 0x03, 0x04, 0x14, 0x00, 0x02, 0x00, 0x08, 0x00, 0x00, 0xbc, 0x98, 0x21 };
|
||||||
|
|
||||||
/* Torrent7z Header Format
|
/* Torrent7z Header Format
|
||||||
http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt
|
http://cpansearch.perl.org/src/BJOERN/Compress-Deflate7-1.0/7zip/DOC/7zFormat.txt
|
||||||
@@ -118,8 +128,8 @@ namespace SabreTools.Library.Data
|
|||||||
06-07 ArchiveVersion (0x00, 0x03)
|
06-07 ArchiveVersion (0x00, 0x03)
|
||||||
The rest is unknown
|
The rest is unknown
|
||||||
*/
|
*/
|
||||||
public static byte[] Torrent7ZipHeader = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, 0x00, 0x03 };
|
public readonly static byte[] Torrent7ZipHeader = new byte[] { 0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c, 0x00, 0x03 };
|
||||||
public static byte[] Torrent7ZipSignature = new byte[] { 0xa9, 0xa9, 0x9f, 0xd1, 0x57, 0x08, 0xa9, 0xd7, 0xea, 0x29, 0x64, 0xb2,
|
public readonly static byte[] Torrent7ZipSignature = new byte[] { 0xa9, 0xa9, 0x9f, 0xd1, 0x57, 0x08, 0xa9, 0xd7, 0xea, 0x29, 0x64, 0xb2,
|
||||||
0x36, 0x1b, 0x83, 0x52, 0x33, 0x00, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x37, 0x7a, 0x5f, 0x30, 0x2e, 0x39, 0x62, 0x65, 0x74, 0x61 };
|
0x36, 0x1b, 0x83, 0x52, 0x33, 0x00, 0x74, 0x6f, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x37, 0x7a, 0x5f, 0x30, 0x2e, 0x39, 0x62, 0x65, 0x74, 0x61 };
|
||||||
|
|
||||||
/* (Torrent)GZ Header Format
|
/* (Torrent)GZ Header Format
|
||||||
@@ -138,7 +148,7 @@ namespace SabreTools.Library.Data
|
|||||||
1C-1F CRC hash
|
1C-1F CRC hash
|
||||||
20-27 Int64 size (mirrored)
|
20-27 Int64 size (mirrored)
|
||||||
*/
|
*/
|
||||||
public static byte[] TorrentGZHeader = new byte[] { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00 };
|
public readonly static byte[] TorrentGZHeader = new byte[] { 0x1f, 0x8b, 0x08, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00 };
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,33 @@
|
|||||||
TorrentZip64 = 45,
|
TorrentZip64 = 45,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Availible CHD codec formats
|
||||||
|
/// </summary>
|
||||||
|
public enum CHDCodecType : uint
|
||||||
|
{
|
||||||
|
CHD_CODEC_NONE = 0,
|
||||||
|
|
||||||
|
// general codecs
|
||||||
|
CHD_CODEC_ZLIB = 0x7a6c6962, // zlib
|
||||||
|
CHD_CODEC_LZMA = 0x6c7a6d61, // lzma
|
||||||
|
CHD_CODEC_HUFFMAN = 0x68756666, // huff
|
||||||
|
CHD_CODEC_FLAC = 0x666c6163, // flac
|
||||||
|
|
||||||
|
// general codecs with CD frontend
|
||||||
|
CHD_CODEC_CD_ZLIB = 0x63647a6c, // cdzl
|
||||||
|
CHD_CODEC_CD_LZMA = 0x63646c7a, // cdlz
|
||||||
|
CHD_CODEC_CD_FLAC = 0x6364666c, // cdfl
|
||||||
|
|
||||||
|
// A/V codecs
|
||||||
|
CHD_CODEC_AVHUFF = 0x61766875, // avhu
|
||||||
|
|
||||||
|
// pseudo-codecs returned by hunk_info
|
||||||
|
CHD_CODEC_SELF = 1, // copy of another hunk
|
||||||
|
CHD_CODEC_PARENT = 2, // copy of a parent's hunk
|
||||||
|
CHD_CODEC_MINI = 3, // legacy "mini" 8-byte repeat
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Compression method based on flag
|
/// Compression method based on flag
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
350
SabreTools.Library/External/CHDFile.cs
vendored
Normal file
350
SabreTools.Library/External/CHDFile.cs
vendored
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
using SabreTools.Library.Data;
|
||||||
|
using SabreTools.Library.Items;
|
||||||
|
using SabreTools.Library.Tools;
|
||||||
|
|
||||||
|
#if MONO
|
||||||
|
using System.IO;
|
||||||
|
#else
|
||||||
|
using BinaryReader = System.IO.BinaryReader;
|
||||||
|
using FileStream = System.IO.FileStream;
|
||||||
|
using SeekOrigin = System.IO.SeekOrigin;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace SabreTools.Library.External
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is code adapted from chd.h and chd.cpp in MAME
|
||||||
|
/// </summary>
|
||||||
|
public class CHDFile
|
||||||
|
{
|
||||||
|
// core parameters from the header
|
||||||
|
private uint m_version; // version of the header
|
||||||
|
private ulong m_logicalbytes; // logical size of the raw CHD data in bytes
|
||||||
|
private ulong m_mapoffset; // offset of map
|
||||||
|
private ulong m_metaoffset; // offset to first metadata bit
|
||||||
|
private uint m_hunkbytes; // size of each raw hunk in bytes
|
||||||
|
private ulong m_hunkcount; // number of hunks represented
|
||||||
|
private uint m_unitbytes; // size of each unit in bytes
|
||||||
|
private ulong m_unitcount; // number of units represented
|
||||||
|
private CHDCodecType[] m_compression = new CHDCodecType[4]; // array of compression types used
|
||||||
|
|
||||||
|
// key offsets within the header
|
||||||
|
private long m_mapoffset_offset; // offset of map offset field
|
||||||
|
private long m_metaoffset_offset;// offset of metaoffset field
|
||||||
|
private long m_sha1_offset; // offset of SHA1 field
|
||||||
|
private long m_rawsha1_offset; // offset of raw SHA1 field
|
||||||
|
private long m_parentsha1_offset;// offset of paren SHA1 field
|
||||||
|
|
||||||
|
// map information
|
||||||
|
uint m_mapentrybytes; // length of each entry in a map
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get internal metadata from a CHD
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="filename">Filename of possible CHD</param>
|
||||||
|
/// <returns>A Disk object with internal SHA-1 on success, null on error, empty Disk otherwise</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Original code had a "writable" param. This is not required for metadata checking
|
||||||
|
/// </remarks>
|
||||||
|
public DatItem GetCHDInfo(string filename)
|
||||||
|
{
|
||||||
|
FileStream fs = FileTools.TryOpenRead(filename);
|
||||||
|
DatItem datItem = GetCHDInfo(fs);
|
||||||
|
fs.Dispose();
|
||||||
|
return datItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get internal metadata from a CHD
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fs">FileStreams of possible CHD</param>
|
||||||
|
/// <returns>A Disk object with internal SHA-1 on success, null on error, empty Disk otherwise</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// Original code had a "writable" param. This is not required for metadata checking
|
||||||
|
/// </remarks>
|
||||||
|
public DatItem GetCHDInfo(FileStream fs)
|
||||||
|
{
|
||||||
|
// Create a blank Disk to populate and return
|
||||||
|
Disk datItem = new Disk();
|
||||||
|
|
||||||
|
// Get a binary reader to make life easier
|
||||||
|
BinaryReader br = new BinaryReader(fs);
|
||||||
|
|
||||||
|
// Read and verify the CHD signature
|
||||||
|
// read the raw header
|
||||||
|
byte[] signature = br.ReadBytes(8);
|
||||||
|
|
||||||
|
// verify the signature
|
||||||
|
bool correct = true;
|
||||||
|
for (int i = 0; i < signature.Length; i++)
|
||||||
|
{
|
||||||
|
correct &= (signature[i] == Constants.CHDSignature[i]);
|
||||||
|
}
|
||||||
|
if (!correct)
|
||||||
|
{
|
||||||
|
// throw CHDERR_INVALID_FILE;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allow writes to the most recent version
|
||||||
|
br.BaseStream.Seek(12, SeekOrigin.Begin);
|
||||||
|
m_version = br.ReadUInt32();
|
||||||
|
|
||||||
|
// read the header if we support it
|
||||||
|
byte[] sha1 = new byte[20];
|
||||||
|
switch (m_version)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
ParseV3Header(br, out sha1);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
ParseV4Header(br, out sha1);
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
ParseV5Header(br, out sha1);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// throw CHDERR_UNSUPPORTED_VERSION;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
datItem.SHA1 = BitConverter.ToString(sha1).Replace("-", string.Empty).ToLowerInvariant();
|
||||||
|
|
||||||
|
return datItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a CHD v3 header, populate the fields, and return the SHA-1
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="br">BinaryReader representing the file to read</param>
|
||||||
|
/// <param name="sha1">Out parameter representing the SHA-1</param>
|
||||||
|
/// <returns>True if the header was parsed properly, false otherwise</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// CHD v3 header layout:
|
||||||
|
/// 0x00-0x07 - CHD signature
|
||||||
|
/// 0x08-0x0B - Header size
|
||||||
|
/// 0x0C-0x0F - [UNUSED]
|
||||||
|
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
|
||||||
|
/// 0x14-0x17 - Compression
|
||||||
|
/// 0x18-0x1B - Hunk count
|
||||||
|
/// 0x1C-0x23 - Logical Bytes
|
||||||
|
/// 0x24-0x2C - Metadata Offset
|
||||||
|
/// ...
|
||||||
|
/// 0x4C-0x4F - Hunk Bytes
|
||||||
|
/// 0x50-0x63 - SHA-1
|
||||||
|
/// 0x64-0x77 - Parent SHA-1
|
||||||
|
/// 0x78-0x87 - Map
|
||||||
|
/// </remarks>
|
||||||
|
private bool ParseV3Header(BinaryReader br, out byte[] sha1)
|
||||||
|
{
|
||||||
|
// Set the blank SHA-1 hash
|
||||||
|
sha1 = null;
|
||||||
|
|
||||||
|
// Set offsets and defaults
|
||||||
|
m_mapoffset_offset = 0;
|
||||||
|
m_rawsha1_offset = 0;
|
||||||
|
m_mapoffset = 120;
|
||||||
|
m_metaoffset_offset = 36;
|
||||||
|
m_sha1_offset = 80;
|
||||||
|
m_parentsha1_offset = 100;
|
||||||
|
m_mapentrybytes = 16;
|
||||||
|
|
||||||
|
// Ensure the proper starting position
|
||||||
|
br.BaseStream.Seek(8, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Verify header length
|
||||||
|
if (br.ReadInt32() != Constants.CHD_V3_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
// throw CHDERR_INVALID_FILE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip over the 0x0C-0x0F block
|
||||||
|
br.ReadUInt32();
|
||||||
|
|
||||||
|
// Read the CHD flags
|
||||||
|
uint flags = br.ReadUInt32();
|
||||||
|
|
||||||
|
// Determine compression
|
||||||
|
switch (br.ReadUInt32())
|
||||||
|
{
|
||||||
|
case 0: m_compression[0] = CHDCodecType.CHD_CODEC_NONE; break;
|
||||||
|
case 1: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
|
||||||
|
case 2: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
|
||||||
|
case 3: m_compression[0] = CHDCodecType.CHD_CODEC_AVHUFF; break;
|
||||||
|
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_compression[1] = m_compression[2] = m_compression[3] = CHDCodecType.CHD_CODEC_NONE;
|
||||||
|
|
||||||
|
m_hunkcount = br.ReadUInt32();
|
||||||
|
m_logicalbytes = br.ReadUInt64();
|
||||||
|
m_metaoffset = br.ReadUInt64();
|
||||||
|
|
||||||
|
br.BaseStream.Seek(76, SeekOrigin.Begin);
|
||||||
|
m_hunkbytes = br.ReadUInt32();
|
||||||
|
|
||||||
|
br.BaseStream.Seek(m_sha1_offset, SeekOrigin.Begin);
|
||||||
|
sha1 = br.ReadBytes(20);
|
||||||
|
|
||||||
|
// guess at the units based on snooping the metadata
|
||||||
|
// m_unitbytes = guess_unitbytes();
|
||||||
|
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a CHD v4 header, populate the fields, and return the SHA-1
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="br">BinaryReader representing the file to read</param>
|
||||||
|
/// <param name="sha1">Out parameter representing the SHA-1</param>
|
||||||
|
/// <returns>True if the header was parsed properly, false otherwise</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// CHD v4 header layout:
|
||||||
|
/// 0x00-0x07 - CHD signature
|
||||||
|
/// 0x08-0x0B - Header size
|
||||||
|
/// 0x0C-0x0F - [UNUSED]
|
||||||
|
/// 0x10-0x13 - Flags (1: Has parent SHA-1, 2: Disallow writes)
|
||||||
|
/// 0x14-0x17 - Compression
|
||||||
|
/// 0x18-0x1B - Hunk count
|
||||||
|
/// 0x1C-0x23 - Logical Bytes
|
||||||
|
/// 0x24-0x2C - Metadata Offset
|
||||||
|
/// ...
|
||||||
|
/// 0x2C-0x2F - Hunk Bytes
|
||||||
|
/// 0x30-0x43 - SHA-1
|
||||||
|
/// 0x44-0x57 - Parent SHA-1
|
||||||
|
/// 0x58-0x6b - Raw SHA-1
|
||||||
|
/// 0x6c-0x7b - Map
|
||||||
|
/// </remarks>
|
||||||
|
private bool ParseV4Header(BinaryReader br, out byte[] sha1)
|
||||||
|
{
|
||||||
|
// Set the blank SHA-1 hash
|
||||||
|
sha1 = null;
|
||||||
|
|
||||||
|
// Set offsets and defaults
|
||||||
|
m_mapoffset_offset = 0;
|
||||||
|
m_metaoffset_offset = 36;
|
||||||
|
m_sha1_offset = 48;
|
||||||
|
m_parentsha1_offset = 68;
|
||||||
|
m_rawsha1_offset = 88;
|
||||||
|
m_mapoffset = 108;
|
||||||
|
m_mapentrybytes = 16;
|
||||||
|
|
||||||
|
// Ensure the proper starting position
|
||||||
|
br.BaseStream.Seek(8, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Verify header length
|
||||||
|
if (br.ReadUInt32() != Constants.CHD_V4_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
// throw CHDERR_INVALID_FILE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read the CHD flags
|
||||||
|
uint flags = br.ReadUInt32();
|
||||||
|
|
||||||
|
// Determine compression
|
||||||
|
switch (br.ReadUInt32())
|
||||||
|
{
|
||||||
|
case 0: m_compression[0] = CHDCodecType.CHD_CODEC_NONE; break;
|
||||||
|
case 1: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
|
||||||
|
case 2: m_compression[0] = CHDCodecType.CHD_CODEC_ZLIB; break;
|
||||||
|
case 3: m_compression[0] = CHDCodecType.CHD_CODEC_AVHUFF; break;
|
||||||
|
default: /* throw CHDERR_UNKNOWN_COMPRESSION; */ return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_compression[1] = m_compression[2] = m_compression[3] = CHDCodecType.CHD_CODEC_NONE;
|
||||||
|
|
||||||
|
m_hunkcount = br.ReadUInt32();
|
||||||
|
m_logicalbytes = br.ReadUInt64();
|
||||||
|
m_metaoffset = br.ReadUInt64();
|
||||||
|
|
||||||
|
br.BaseStream.Seek(44, SeekOrigin.Begin);
|
||||||
|
m_hunkbytes = br.ReadUInt32();
|
||||||
|
|
||||||
|
br.BaseStream.Seek(m_sha1_offset, SeekOrigin.Begin);
|
||||||
|
sha1 = br.ReadBytes(20);
|
||||||
|
|
||||||
|
// guess at the units based on snooping the metadata
|
||||||
|
// m_unitbytes = guess_unitbytes();
|
||||||
|
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Parse a CHD v5 header, populate the fields, and return the SHA-1
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="br">BinaryReader representing the file to read</param>
|
||||||
|
/// <param name="sha1">Out parameter representing the SHA-1</param>
|
||||||
|
/// <returns>True if the header was parsed properly, false otherwise</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// CHD v5 header layout:
|
||||||
|
/// 0x00-0x07 - CHD signature
|
||||||
|
/// 0x08-0x0B - Header size
|
||||||
|
/// 0x0C-0x0F - [UNUSED]
|
||||||
|
/// 0x10-0x13 - Compression format 1
|
||||||
|
/// 0x14-0x17 - Compression format 2
|
||||||
|
/// 0x18-0x1B - Compression format 3
|
||||||
|
/// 0x1C-0x1F - Compression format 4
|
||||||
|
/// 0x18-0x1B - Hunk count
|
||||||
|
/// 0x20-0x27 - Logical Bytes
|
||||||
|
/// 0x28-0x2F - Map Offset
|
||||||
|
/// 0x30-0x37 - Metadata Offset
|
||||||
|
/// 0x38-0x3B - Hunk Bytes
|
||||||
|
/// 0x3C-0x3F - Unit Bytes
|
||||||
|
/// 0x40-0x53 - Raw SHA-1
|
||||||
|
/// 0x54-0x67 - SHA-1
|
||||||
|
/// 0x68-0x7b - Parent SHA-1
|
||||||
|
/// </remarks>
|
||||||
|
private bool ParseV5Header(BinaryReader br, out byte[] sha1)
|
||||||
|
{
|
||||||
|
// Set the blank SHA-1 hash
|
||||||
|
sha1 = null;
|
||||||
|
|
||||||
|
// Set offsets and defaults
|
||||||
|
m_mapoffset_offset = 40;
|
||||||
|
m_metaoffset_offset = 48;
|
||||||
|
m_rawsha1_offset = 64;
|
||||||
|
m_sha1_offset = 84;
|
||||||
|
m_parentsha1_offset = 104;
|
||||||
|
|
||||||
|
// Ensure the proper starting position
|
||||||
|
br.BaseStream.Seek(8, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
// Verify header length
|
||||||
|
if (br.ReadUInt32() != Constants.CHD_V5_HEADER_SIZE)
|
||||||
|
{
|
||||||
|
// throw CHDERR_INVALID_FILE
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine compression
|
||||||
|
m_compression[0] = (CHDCodecType)br.ReadUInt32();
|
||||||
|
m_compression[1] = (CHDCodecType)br.ReadUInt32();
|
||||||
|
m_compression[2] = (CHDCodecType)br.ReadUInt32();
|
||||||
|
m_compression[3] = (CHDCodecType)br.ReadUInt32();
|
||||||
|
|
||||||
|
m_logicalbytes = br.ReadUInt64();
|
||||||
|
m_mapoffset = br.ReadUInt64();
|
||||||
|
m_metaoffset = br.ReadUInt64();
|
||||||
|
m_hunkbytes = br.ReadUInt32();
|
||||||
|
m_hunkcount = (m_logicalbytes + m_hunkbytes - 1) / m_hunkbytes;
|
||||||
|
m_unitbytes = br.ReadUInt32();
|
||||||
|
m_unitcount = (m_logicalbytes + m_unitbytes - 1) / m_unitbytes;
|
||||||
|
|
||||||
|
// m_allow_writes = !compressed();
|
||||||
|
|
||||||
|
// determine properties of map entries
|
||||||
|
// m_mapentrybytes = compressed() ? 12 : 4;
|
||||||
|
|
||||||
|
br.BaseStream.Seek(m_sha1_offset, SeekOrigin.Begin);
|
||||||
|
sha1 = br.ReadBytes(20);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -130,6 +130,7 @@
|
|||||||
<Compile Include="DatFiles\SabreDat.cs" />
|
<Compile Include="DatFiles\SabreDat.cs" />
|
||||||
<Compile Include="DatFiles\SeparatedValue.cs" />
|
<Compile Include="DatFiles\SeparatedValue.cs" />
|
||||||
<Compile Include="DatFiles\SoftwareList.cs" />
|
<Compile Include="DatFiles\SoftwareList.cs" />
|
||||||
|
<Compile Include="External\CHDFile.cs" />
|
||||||
<Compile Include="External\CoreRarArchive.cs" />
|
<Compile Include="External\CoreRarArchive.cs" />
|
||||||
<Compile Include="External\NaturalSort\NaturalComparer.cs" />
|
<Compile Include="External\NaturalSort\NaturalComparer.cs" />
|
||||||
<Compile Include="External\NaturalSort\NaturalReversedComparer.cs" />
|
<Compile Include="External\NaturalSort\NaturalReversedComparer.cs" />
|
||||||
|
|||||||
@@ -644,7 +644,7 @@ namespace SabreTools.Library.Tools
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stream entryStream = entry.OpenEntryStream();
|
Stream entryStream = entry.OpenEntryStream();
|
||||||
Rom sevenZipEntryRom = FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
Rom sevenZipEntryRom = (Rom)FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
||||||
sevenZipEntryRom.Name = entry.Key;
|
sevenZipEntryRom.Name = entry.Key;
|
||||||
sevenZipEntryRom.MachineName = gamename;
|
sevenZipEntryRom.MachineName = gamename;
|
||||||
sevenZipEntryRom.Date = (date && entry.LastModifiedTime != null ? entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss") : null);
|
sevenZipEntryRom.Date = (date && entry.LastModifiedTime != null ? entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss") : null);
|
||||||
@@ -677,7 +677,7 @@ namespace SabreTools.Library.Tools
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
GZipStream gzstream = new GZipStream(FileTools.TryOpenRead(input), Ionic.Zlib.CompressionMode.Decompress);
|
GZipStream gzstream = new GZipStream(FileTools.TryOpenRead(input), Ionic.Zlib.CompressionMode.Decompress);
|
||||||
Rom gzipEntryRom = FileTools.GetStreamInfo(gzstream, gzstream.Length, omitFromScan: omitFromScan);
|
Rom gzipEntryRom = (Rom)FileTools.GetStreamInfo(gzstream, gzstream.Length, omitFromScan: omitFromScan);
|
||||||
gzipEntryRom.Name = gzstream.FileName;
|
gzipEntryRom.Name = gzstream.FileName;
|
||||||
gzipEntryRom.MachineName = gamename;
|
gzipEntryRom.MachineName = gamename;
|
||||||
gzipEntryRom.Date = (date && gzstream.LastModified != null ? gzstream.LastModified?.ToString("yyyy/MM/dd hh:mm:ss") : null);
|
gzipEntryRom.Date = (date && gzstream.LastModified != null ? gzstream.LastModified?.ToString("yyyy/MM/dd hh:mm:ss") : null);
|
||||||
@@ -709,7 +709,7 @@ namespace SabreTools.Library.Tools
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stream entryStream = entry.OpenEntryStream();
|
Stream entryStream = entry.OpenEntryStream();
|
||||||
Rom rarEntryRom = FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
Rom rarEntryRom = (Rom)FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
||||||
rarEntryRom.Name = entry.Key;
|
rarEntryRom.Name = entry.Key;
|
||||||
rarEntryRom.MachineName = gamename;
|
rarEntryRom.MachineName = gamename;
|
||||||
rarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
rarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
||||||
@@ -745,7 +745,7 @@ namespace SabreTools.Library.Tools
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stream entryStream = entry.OpenEntryStream();
|
Stream entryStream = entry.OpenEntryStream();
|
||||||
Rom tarEntryRom = FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
Rom tarEntryRom = (Rom)FileTools.GetStreamInfo(entryStream, entry.Size, omitFromScan: omitFromScan);
|
||||||
tarEntryRom.Name = entry.Key;
|
tarEntryRom.Name = entry.Key;
|
||||||
tarEntryRom.MachineName = gamename;
|
tarEntryRom.MachineName = gamename;
|
||||||
tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
tarEntryRom.Date = entry.LastModifiedTime?.ToString("yyyy/MM/dd hh:mm:ss");
|
||||||
@@ -802,7 +802,7 @@ namespace SabreTools.Library.Tools
|
|||||||
// Otherwise, use the stream directly
|
// Otherwise, use the stream directly
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Rom zipEntryRom = FileTools.GetStreamInfo(readStream, (long)zf.Entries[i].UncompressedSize, omitFromScan: omitFromScan);
|
Rom zipEntryRom = (Rom)FileTools.GetStreamInfo(readStream, (long)zf.Entries[i].UncompressedSize, omitFromScan: omitFromScan);
|
||||||
zipEntryRom.Name = zf.Entries[i].FileName;
|
zipEntryRom.Name = zf.Entries[i].FileName;
|
||||||
zipEntryRom.MachineName = gamename;
|
zipEntryRom.MachineName = gamename;
|
||||||
string convertedDate = Style.ConvertMsDosTimeFormatToDateTime(zf.Entries[i].LastMod).ToString("yyyy/MM/dd hh:mm:ss");
|
string convertedDate = Style.ConvertMsDosTimeFormatToDateTime(zf.Entries[i].LastMod).ToString("yyyy/MM/dd hh:mm:ss");
|
||||||
@@ -2216,7 +2216,7 @@ namespace SabreTools.Library.Tools
|
|||||||
outDir = Path.GetFullPath(outDir);
|
outDir = Path.GetFullPath(outDir);
|
||||||
|
|
||||||
// Now get the Rom info for the file so we have hashes and size
|
// Now get the Rom info for the file so we have hashes and size
|
||||||
Rom rom = FileTools.GetStreamInfo(inputStream, inputStream.Length, keepReadOpen: true);
|
Rom rom = (Rom)FileTools.GetStreamInfo(inputStream, inputStream.Length, keepReadOpen: true);
|
||||||
|
|
||||||
// Get the output file name
|
// Get the output file name
|
||||||
string outfile = null;
|
string outfile = null;
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ namespace SabreTools.Library.Tools
|
|||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// TODO: Add CHD parsing logic here to get internal hash data
|
/// TODO: Add CHD parsing logic here to get internal hash data
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
public static Rom GetFileInfo(string input, Hash omitFromScan = 0x0,
|
public static DatItem GetFileInfo(string input, Hash omitFromScan = 0x0,
|
||||||
long offset = 0, bool date = false, string header = null)
|
long offset = 0, bool date = false, string header = null)
|
||||||
{
|
{
|
||||||
// Add safeguard if file doesn't exist
|
// Add safeguard if file doesn't exist
|
||||||
@@ -240,7 +240,7 @@ namespace SabreTools.Library.Tools
|
|||||||
|
|
||||||
// Transform the stream and get the information from it
|
// Transform the stream and get the information from it
|
||||||
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
rule.TransformStream(inputStream, outputStream, keepReadOpen: false, keepWriteOpen: true);
|
||||||
rom = GetStreamInfo(outputStream, outputStream.Length, omitFromScan: omitFromScan, keepReadOpen: false);
|
rom = (Rom)GetStreamInfo(outputStream, outputStream.Length, omitFromScan: omitFromScan, keepReadOpen: false);
|
||||||
|
|
||||||
// Dispose of the streams
|
// Dispose of the streams
|
||||||
outputStream.Dispose();
|
outputStream.Dispose();
|
||||||
@@ -250,13 +250,13 @@ namespace SabreTools.Library.Tools
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
long length = new FileInfo(input).Length;
|
long length = new FileInfo(input).Length;
|
||||||
rom = GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
rom = (Rom)GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
long length = new FileInfo(input).Length;
|
long length = new FileInfo(input).Length;
|
||||||
rom = GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
rom = (Rom)GetStreamInfo(TryOpenRead(input), length, omitFromScan, offset, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add unique data from the file
|
// Add unique data from the file
|
||||||
@@ -409,7 +409,7 @@ namespace SabreTools.Library.Tools
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Now add the information to the database if it's not already there
|
// Now add the information to the database if it's not already there
|
||||||
Rom rom = GetFileInfo(newfile);
|
Rom rom = (Rom)GetFileInfo(newfile);
|
||||||
DatabaseTools.AddHeaderToDatabase(hstr, rom.SHA1, rule.SourceFile);
|
DatabaseTools.AddHeaderToDatabase(hstr, rom.SHA1, rule.SourceFile);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -508,7 +508,7 @@ namespace SabreTools.Library.Tools
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First, get the SHA-1 hash of the file
|
// First, get the SHA-1 hash of the file
|
||||||
Rom rom = GetFileInfo(file);
|
Rom rom = (Rom)GetFileInfo(file);
|
||||||
|
|
||||||
// Retrieve a list of all related headers from the database
|
// Retrieve a list of all related headers from the database
|
||||||
List<string> headers = DatabaseTools.RetrieveHeadersFromDatabase(rom.SHA1);
|
List<string> headers = DatabaseTools.RetrieveHeadersFromDatabase(rom.SHA1);
|
||||||
@@ -733,7 +733,7 @@ namespace SabreTools.Library.Tools
|
|||||||
/// <param name="offset">Set a >0 number for getting hash for part of the file, 0 otherwise (default)</param>
|
/// <param name="offset">Set a >0 number for getting hash for part of the file, 0 otherwise (default)</param>
|
||||||
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
/// <param name="keepReadOpen">True if the underlying read stream should be kept open, false otherwise</param>
|
||||||
/// <returns>Populated DatItem object if success, empty one on error</returns>
|
/// <returns>Populated DatItem object if success, empty one on error</returns>
|
||||||
public static Rom GetStreamInfo(Stream input, long size, Hash omitFromScan = 0x0,
|
public static DatItem GetStreamInfo(Stream input, long size, Hash omitFromScan = 0x0,
|
||||||
long offset = 0, bool keepReadOpen = false)
|
long offset = 0, bool keepReadOpen = false)
|
||||||
{
|
{
|
||||||
Rom rom = new Rom
|
Rom rom = new Rom
|
||||||
|
|||||||
Reference in New Issue
Block a user