(YPF): detect archives embedded into EXE files.

This commit is contained in:
morkt 2018-02-11 10:50:09 +04:00
parent 16720db6e6
commit 43fc625d81

View File

@ -2,7 +2,7 @@
//! \date Mon Jul 14 14:40:06 2014 //! \date Mon Jul 14 14:40:06 2014
//! \brief YPF resource format implementation. //! \brief YPF resource format implementation.
// //
// Copyright (C) 2014-2015 by morkt // Copyright (C) 2014-2018 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
@ -103,27 +103,42 @@ namespace GameRes.Formats.YuRis
public override bool IsHierarchic { get { return true; } } public override bool IsHierarchic { get { return true; } }
public override bool CanWrite { get { return true; } } public override bool CanWrite { get { return true; } }
static public Dictionary<string, YpfScheme> KnownSchemes = new Dictionary<string, YpfScheme>(); public YpfOpener ()
{
Signatures = new uint[] { 0x00465059, 0 };
}
static public Dictionary<string, YpfScheme> KnownSchemes { get { return DefaultScheme.KnownSchemes; } }
static YuRisScheme DefaultScheme = new YuRisScheme { KnownSchemes = new Dictionary<string, YpfScheme>() };
public override ResourceScheme Scheme public override ResourceScheme Scheme
{ {
get { return new YuRisScheme { KnownSchemes = KnownSchemes }; } get { return DefaultScheme; }
set { KnownSchemes = ((YuRisScheme)value).KnownSchemes; } set { DefaultScheme = (YuRisScheme)value; }
} }
public override ArcFile TryOpen (ArcView file) public override ArcFile TryOpen (ArcView file)
{ {
uint version = file.View.ReadUInt32 (4); long ypf_offset = 0;
uint count = file.View.ReadUInt32 (8); if (file.View.AsciiEqual (0, "MZ"))
uint dir_size = file.View.ReadUInt32 (12); {
if (dir_size < count * 0x17 || count > 0xfffff) ypf_offset = FindYser (file);
}
if (!file.View.AsciiEqual (ypf_offset, "YPF\0"))
return null; return null;
if (dir_size > file.View.Reserve (0x20, dir_size))
uint version = file.View.ReadUInt32 (ypf_offset+4);
int count = file.View.ReadInt32 (ypf_offset+8);
uint dir_size = file.View.ReadUInt32 (ypf_offset+12);
if (!IsSaneCount (count) || dir_size < count * 0x17)
return null;
if (dir_size > file.View.Reserve (ypf_offset+0x20, dir_size))
return null; return null;
var parser = new Parser (file, version, count, dir_size); var parser = new Parser (file, version, count, dir_size);
var scheme = QueryEncryptionScheme (file.Name, version); var scheme = QueryEncryptionScheme (file.Name, version);
var dir = parser.ScanDir (scheme); var dir = parser.ScanDir (scheme, ypf_offset);
if (null == dir || 0 == dir.Count) if (null == dir || 0 == dir.Count)
return null; return null;
@ -192,6 +207,16 @@ namespace GameRes.Formats.YuRis
return scheme; return scheme;
} }
internal long FindYser (ArcView file)
{
var exe = new ExeFile (file);
var offset = exe.FindAsciiString (exe.Overlay, "YSER", 0x10);
if (-1 == offset)
return 0;
uint header_size = file.View.ReadUInt32 (offset+4);
return offset + header_size;
}
delegate uint ChecksumFunc (byte[] data); delegate uint ChecksumFunc (byte[] data);
public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options, public override void Create (Stream output, IEnumerable<Entry> list, ResourceOptions options,
@ -358,7 +383,7 @@ namespace GameRes.Formats.YuRis
uint* header = (uint*)raw; uint* header = (uint*)raw;
uint version = header[1]; uint version = header[1];
int first_item, last_item; int first_item, last_item;
if (version >= 0x1CE || 0x12C == version || 0x19A == version) if (version >= 0x1CE || 0x12C == version || 0x19A == version || 0x1C3 == version)
{ {
first_item = 3; first_item = 3;
last_item = 7; last_item = 7;
@ -404,10 +429,10 @@ namespace GameRes.Formats.YuRis
{ {
ArcView m_file; ArcView m_file;
uint m_version; uint m_version;
uint m_count; int m_count;
uint m_dir_size; uint m_dir_size;
public Parser (ArcView file, uint version, uint count, uint dir_size) public Parser (ArcView file, uint version, int count, uint dir_size)
{ {
m_file = file; m_file = file;
m_count = count; m_count = count;
@ -417,15 +442,15 @@ namespace GameRes.Formats.YuRis
// int32-name_checksum, byte-name_count, *-name, byte-file_type // int32-name_checksum, byte-name_count, *-name, byte-file_type
// byte-pack_flag, int32-size, int32-packed_size, int32-offset, int32-file_checksum // byte-pack_flag, int32-size, int32-packed_size, int32-offset, int32-file_checksum
public List<Entry> ScanDir (YpfScheme scheme) public List<Entry> ScanDir (YpfScheme scheme, long base_offset = 0)
{ {
uint dir_offset = 0x20; long dir_offset = base_offset + 0x20;
uint dir_remaining = m_dir_size; uint dir_remaining = m_dir_size;
var dir = new List<Entry> ((int)m_count); var dir = new List<Entry> (m_count);
byte key = scheme.Key; byte key = scheme.Key;
bool guess_key = scheme.GuessKey; bool guess_key = scheme.GuessKey;
uint extra_size = 0x12 + scheme.ExtraHeaderSize; uint extra_size = 0x12 + scheme.ExtraHeaderSize;
for (uint num = 0; num < m_count; ++num) for (int num = 0; num < m_count; ++num)
{ {
if (dir_remaining < 5+extra_size) if (dir_remaining < 5+extra_size)
return null; return null;
@ -482,7 +507,7 @@ namespace GameRes.Formats.YuRis
entry.IsPacked = 0 != m_file.View.ReadByte (dir_offset+1); entry.IsPacked = 0 != m_file.View.ReadByte (dir_offset+1);
entry.UnpackedSize = m_file.View.ReadUInt32 (dir_offset+2); entry.UnpackedSize = m_file.View.ReadUInt32 (dir_offset+2);
entry.Size = m_file.View.ReadUInt32 (dir_offset+6); entry.Size = m_file.View.ReadUInt32 (dir_offset+6);
entry.Offset = m_file.View.ReadUInt32 (dir_offset+10); entry.Offset = m_file.View.ReadUInt32 (dir_offset+10) + base_offset;
if (entry.CheckPlacement (m_file.MaxOffset)) if (entry.CheckPlacement (m_file.MaxOffset))
dir.Add (entry); dir.Add (entry);
dir_offset += extra_size; dir_offset += extra_size;