diff --git a/ArcFormats/Silky/ArcAi6Win.cs b/ArcFormats/Silky/ArcAi6Win.cs index 72e8269e1..77f5c06e1 100644 --- a/ArcFormats/Silky/ArcAi6Win.cs +++ b/ArcFormats/Silky/ArcAi6Win.cs @@ -27,7 +27,9 @@ using System.Collections.Generic; using System.ComponentModel.Composition; using System.IO; +using System.Linq; using GameRes.Compression; +using GameRes.Formats.Strings; using GameRes.Utility; namespace GameRes.Formats.Silky @@ -35,66 +37,170 @@ namespace GameRes.Formats.Silky [Export(typeof(ArchiveFormat))] public class Ai6Opener : ArchiveFormat { - public override string Tag { get { return "ARC/AI6WIN"; } } + public override string Tag { get { return "ARC/AI6WIN"; } } public override string Description { get { return "AI6WIN engine resource archive"; } } - public override uint Signature { get { return 0; } } - public override bool IsHierarchic { get { return true; } } - public override bool CanWrite { get { return false; } } + public override uint Signature { get { return 0; } } + public override bool IsHierarchic { get { return true; } } + public override bool CanWrite { get { return true; } } - public Ai6Opener () + public Ai6Opener() { Extensions = new string[] { "arc" }; } - public override ArcFile TryOpen (ArcView file) + public override ArcFile TryOpen(ArcView file) { - int count = file.View.ReadInt32 (0); - if (!IsSaneCount (count)) + int count = file.View.ReadInt32(0); + if (!IsSaneCount(count)) return null; long index_offset = 4; uint index_size = (uint)(count * (0x104 + 12)); - if (index_size > file.View.Reserve (index_offset, index_size)) + if (index_size > file.View.Reserve(index_offset, index_size)) return null; var name_buffer = new byte[0x104]; var dir = new List(); for (int i = 0; i < count; ++i) { - file.View.Read (index_offset, name_buffer, 0, (uint)name_buffer.Length); - int name_length = Array.IndexOf (name_buffer, 0); + file.View.Read(index_offset, name_buffer, 0, (uint)name_buffer.Length); + int name_length = Array.IndexOf(name_buffer, 0); if (0 == name_length) return null; if (-1 == name_length) name_length = name_buffer.Length; - byte key = (byte)(name_length+1); + byte key = (byte)(name_length + 1); for (int j = 0; j < name_length; ++j) { name_buffer[j] -= key--; char c = (char)name_buffer[j]; - if (VFS.InvalidFileNameChars.Contains (c) && c != '/') + if (VFS.InvalidFileNameChars.Contains(c) && c != '/') return null; } - var name = Encodings.cp932.GetString (name_buffer, 0, name_length); + var name = Encodings.cp932.GetString(name_buffer, 0, name_length); index_offset += 0x104; - var entry = FormatCatalog.Instance.Create (name); - entry.Size = Binary.BigEndian (file.View.ReadUInt32 (index_offset)); - entry.UnpackedSize = Binary.BigEndian (file.View.ReadUInt32 (index_offset+4)); - entry.Offset = Binary.BigEndian (file.View.ReadUInt32 (index_offset+8)); - if (entry.Offset < index_size+4 || !entry.CheckPlacement (file.MaxOffset)) + var entry = FormatCatalog.Instance.Create(name); + entry.Size = Binary.BigEndian(file.View.ReadUInt32(index_offset)); + entry.UnpackedSize = Binary.BigEndian(file.View.ReadUInt32(index_offset + 4)); + entry.Offset = Binary.BigEndian(file.View.ReadUInt32(index_offset + 8)); + if (entry.Offset < index_size + 4 || !entry.CheckPlacement(file.MaxOffset)) return null; entry.IsPacked = entry.Size != entry.UnpackedSize; - dir.Add (entry); + dir.Add(entry); index_offset += 12; } - return new ArcFile (file, this, dir); + return new ArcFile(file, this, dir); } - public override Stream OpenEntry (ArcFile arc, Entry entry) + public override Stream OpenEntry(ArcFile arc, Entry entry) { var pent = entry as PackedEntry; if (null == pent || !pent.IsPacked) - return base.OpenEntry (arc, entry); - var input = arc.File.CreateStream (entry.Offset, entry.Size); - return new LzssStream (input); + return base.OpenEntry(arc, entry); + var input = arc.File.CreateStream(entry.Offset, entry.Size); + return new LzssStream(input); + } + + public override void Create(Stream output, IEnumerable list, ResourceOptions options, + EntryCallback callback) + { + int count = list.Count(); + if (0 == count) + throw new InvalidOperationException("Archive is empty"); + + uint index_size = (uint)(count * (0x104 + 12)); + uint data_offset = 4 + index_size; + + if (null != callback) + callback(count + 1, null, null); + + var entries = new List(count); + uint current_offset = data_offset; + int callback_count = 0; + + foreach (var entry in list) + { + if (null != callback) + callback(callback_count++, entry, arcStrings.MsgAddingFile); + + string name = entry.Name; + if (name.Contains("\\")) + name = name.Replace("\\", "/"); + + try + { + long fileSize = 0; + using (var input = File.OpenRead(entry.Name)) + { + fileSize = input.Length; + } + + if (fileSize > uint.MaxValue) + throw new FileLoadException("File is too large for this format."); + + var index_entry = new IndexEntry + { + SourcePath = entry.Name, + ArchiveName = name, + Size = (uint)fileSize, + UnpackedSize = (uint)fileSize, + Offset = current_offset + }; + + entries.Add(index_entry); + current_offset += (uint)fileSize; + } + catch (Exception X) + { + // 修复点:此处改用硬编码字符串或尝试 MsgFileError + throw new InvalidFileName(entry.Name, "Error opening file", X); + } + } + + if (null != callback) + callback(callback_count++, null, arcStrings.MsgWritingIndex); + + using (var writer = new BinaryWriter(output)) + { + writer.Write((uint)count); + + foreach (var entry in entries) + { + var name_bytes = Encodings.cp932.GetBytes(entry.ArchiveName); + var name_buf = new byte[0x104]; + + int copyLength = Math.Min(name_bytes.Length, name_buf.Length); + Array.Copy(name_bytes, name_buf, copyLength); + + byte key = (byte)(name_bytes.Length + 1); + + for (int i = 0; i < copyLength; ++i) + { + name_buf[i] = (byte)((name_buf[i] + key) & 0xFF); + key--; + } + + writer.Write(name_buf); + writer.Write(Binary.BigEndian(entry.Size)); + writer.Write(Binary.BigEndian(entry.UnpackedSize)); + writer.Write(Binary.BigEndian(entry.Offset)); + } + + foreach (var entry in entries) + { + using (var input = File.OpenRead(entry.SourcePath)) + { + input.CopyTo(output); + } + } + } + } + + private class IndexEntry + { + public string SourcePath { get; set; } + public string ArchiveName { get; set; } + public uint Size { get; set; } + public uint UnpackedSize { get; set; } + public uint Offset { get; set; } } } }