(VFF): recognize multi-part archives.

This commit is contained in:
morkt 2018-08-09 08:15:57 +04:00
parent 111481c4ac
commit 48d741c37c

View File

@ -2,7 +2,7 @@
//! \date Wed Jun 08 00:27:36 2016 //! \date Wed Jun 08 00:27:36 2016
//! \brief LiveMaker resource archive. //! \brief LiveMaker resource archive.
// //
// Copyright (C) 2016 by morkt // Copyright (C) 2016-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
@ -51,13 +51,14 @@ namespace GameRes.Formats.LiveMaker
{ {
uint base_offset = 0; uint base_offset = 0;
ArcView index_file = file; ArcView index_file = file;
ArcView extra_file = null;
try try
{ {
// possible filesystem structure: // possible filesystem structure:
// game.dat -- main archive body // game.dat -- main archive body
// game.001 -- [optional] extra part
// game.ext -- [optional] separate index (could be included into the main body) // game.ext -- [optional] separate index (could be included into the main body)
// game.001 -- [optional] extra parts
// game.002
// ...
uint signature = index_file.View.ReadUInt32 (0); uint signature = index_file.View.ReadUInt32 (0);
if (file.Name.HasExtension (".exe") if (file.Name.HasExtension (".exe")
@ -88,36 +89,29 @@ namespace GameRes.Formats.LiveMaker
if (null == dir) if (null == dir)
return null; return null;
long max_offset = file.MaxOffset; long max_offset = file.MaxOffset;
for (int i = 0; i < dir.Count; ++i) var parts = new List<ArcView>();
try
{ {
if (!dir[i].CheckPlacement (max_offset)) for (int i = 1; i < 100; ++i)
{ {
if (extra_file != null) var ext = string.Format (".{0:D3}", i);
{ var part_filename = Path.ChangeExtension (file.Name, ext);
// remove entries that don't fit into game.dat+game.001 if (!VFS.FileExists (part_filename))
int discard = dir.Count - i;
Trace.WriteLine (string.Format ("{0} entries didn't fit and were discarded", discard), "[vff]");
dir.RemoveRange (i, discard);
break; break;
} var arc_file = VFS.OpenView (part_filename);
var extra_filename = Path.ChangeExtension (file.Name, ".001"); max_offset += arc_file.MaxOffset;
if (!VFS.FileExists (extra_filename)) parts.Add (arc_file);
return null;
extra_file = VFS.OpenView (extra_filename);
max_offset += extra_file.MaxOffset;
if (!dir[i].CheckPlacement (max_offset))
return null;
} }
} }
if (null == extra_file) catch
{
foreach (var part in parts)
part.Dispose();
throw;
}
if (0 == parts.Count)
return new ArcFile (file, this, dir); return new ArcFile (file, this, dir);
return new VffArchive (file, this, dir, extra_file); return new MultiFileArchive (file, this, dir, parts);
}
catch
{
if (extra_file != null)
extra_file.Dispose();
throw;
} }
finally finally
{ {
@ -128,7 +122,7 @@ namespace GameRes.Formats.LiveMaker
public override Stream OpenEntry (ArcFile arc, Entry entry) public override Stream OpenEntry (ArcFile arc, Entry entry)
{ {
var vff = arc as VffArchive; var vff = arc as MultiFileArchive;
Stream input = null; Stream input = null;
if (vff != null) if (vff != null)
input = vff.OpenStream (entry); input = vff.OpenStream (entry);
@ -218,43 +212,4 @@ namespace GameRes.Formats.LiveMaker
m_current = 0; m_current = 0;
} }
} }
internal class VffArchive : ArcFile
{
readonly ArcView ExtraFile;
public VffArchive (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, ArcView extra_file)
: base (arc, impl, dir)
{
ExtraFile = extra_file;
}
internal Stream OpenStream (Entry entry)
{
if (entry.Offset < File.MaxOffset && entry.Offset+entry.Size > File.MaxOffset)
{
var first_part = File.View.ReadBytes (entry.Offset, entry.Size);
var second_part = ExtraFile.CreateStream (0, entry.Size - (uint)first_part.Length);
return new PrefixStream (first_part, second_part);
}
else if (entry.Offset >= File.MaxOffset)
{
return ExtraFile.CreateStream (entry.Offset, entry.Size);
}
else
return File.CreateStream (entry.Offset, entry.Size);
}
bool _vff_disposed = false;
protected override void Dispose (bool disposing)
{
if (!_vff_disposed)
{
if (disposing && ExtraFile != null)
ExtraFile.Dispose();
_vff_disposed = true;
}
base.Dispose (disposing);
}
}
} }