Example #1
0
        /// <summary>
        /// Adds or replace a given file with name filename with data from sourceStream
        /// </summary>
        /// <param name="filename">The filename used inside the pak</param>
        /// <param name="sourceStream">Source Stream of file to be added</param>
        /// <param name="CreateTime">Time to use as original file creation time</param>
        /// <param name="ModifyTime">Time to use as last modified time</param>
        /// <param name="autoSpareSpace">Enable adding 25% of the sourceStream size as padding when not replacing a file</param>
        /// <param name="pfi">AAPakFileInfo of the newly added or modified file</param>
        /// <returns></returns>
        public bool AddFileFromStream(string filename, Stream sourceStream, DateTime CreateTime, DateTime ModifyTime, bool autoSpareSpace, out AAPakFileInfo pfi)
        {
            pfi = nullAAPakFileInfo;
            if (readOnly)
            {
                return(false);
            }

            bool addAsNew = true;

            // Try to find the existing file
            if (GetFileByName(filename, ref pfi))
            {
                var reservedSizeMax = pfi.size + pfi.paddingSize;
                addAsNew = (sourceStream.Length > reservedSizeMax);
                // Bugfix: If we have inssuficient space, make sure to delete the old file first as well
                if (addAsNew)
                {
                    DeleteFile(pfi);
                }
            }

            if (addAsNew)
            {
                return(AddAsNewFile(filename, sourceStream, CreateTime, ModifyTime, autoSpareSpace, out pfi));
            }
            else
            {
                return(ReplaceFile(ref pfi, sourceStream, ModifyTime));
            }
        }
Example #2
0
 /// <summary>
 /// Exports a given file as a Stream
 /// </summary>
 /// <param name="file">AAPakFileInfo of the file to be exported</param>
 /// <returns>Returns a SubStream of file within the pak</returns>
 public Stream ExportFileAsStream(AAPakFileInfo file)
 {
     return(new SubStream(_gpFileStream, file.offset, file.size));
 }
Example #3
0
        /// <summary>
        /// Writes current files info back into FAT (encrypted)
        /// </summary>
        /// <returns>Returns true on success</returns>
        public bool WriteToFAT()
        {
            // Read all File Table Data into Memory
            FAT.SetLength(0);

            int          bufSize = 0x150;                     // Marshal.SizeOf(typeof(AAPakFileInfo));
            MemoryStream ms      = new MemoryStream(bufSize); // Could probably do without the intermediate memorystream, but it's easier to process
            BinaryWriter writer  = new BinaryWriter(ms);

            // Init File Counts
            var totalFileCount = _owner.files.Count + _owner.extraFiles.Count;
            var filesToGo      = _owner.files.Count;
            var extrasToGo     = _owner.extraFiles.Count;
            int fileIndex      = 0;
            int extrasIndex    = 0;

            for (int i = 0; i < totalFileCount; i++)
            {
                ms.Position = 0;

                AAPakFileInfo pfi = null;

                if (_owner.PakType == PakFileType.PakTypeA)
                {
                    // TypeA has files first, extra files after that
                    if (filesToGo > 0)
                    {
                        filesToGo--;
                        pfi = _owner.files[fileIndex];
                        fileIndex++;
                    }
                    else
                    if (extrasToGo > 0)
                    {
                        extrasToGo--;
                        pfi = _owner.extraFiles[extrasIndex];
                        extrasIndex++;
                    }
                    else
                    {
                        // If we get here, your PC cannot math and something went wrong
                        pfi = null;
                        break;
                    }
                }
                else
                if (_owner.PakType == PakFileType.PakTypeB)
                {
                    // TypeA has files first, extra files after that
                    if (extrasToGo > 0)
                    {
                        extrasToGo--;
                        pfi = _owner.extraFiles[extrasIndex];
                        extrasIndex++;
                    }
                    else
                    if (filesToGo > 0)
                    {
                        filesToGo--;
                        pfi = _owner.files[fileIndex];
                        fileIndex++;
                    }
                    else
                    {
                        // If we get here, your PC cannot math and something went wrong
                        pfi = null;
                        break;
                    }
                }
                else
                {
                    // Unsupported Type somehow
                }

                if (_owner.PakType == PakFileType.PakTypeA)
                {
                    // Manually write the string for filename
                    for (int c = 0; c < 0x108; c++)
                    {
                        byte ch = 0;
                        if (c < pfi.name.Length)
                        {
                            ch = (byte)pfi.name[c];
                        }
                        writer.Write(ch);
                    }
                    writer.Write(pfi.offset);
                    writer.Write(pfi.size);
                    writer.Write(pfi.sizeDuplicate);
                    writer.Write(pfi.paddingSize);
                    writer.Write(pfi.md5);
                    writer.Write(pfi.dummy1);
                    writer.Write(pfi.createTime);
                    writer.Write(pfi.modifyTime);
                    writer.Write(pfi.dummy2);
                }
                else
                if (_owner.PakType == PakFileType.PakTypeB)
                {
                    writer.Write(pfi.paddingSize);
                    writer.Write(pfi.md5);
                    writer.Write(pfi.dummy1);
                    writer.Write(pfi.size);

                    // Manually write the string for filename
                    for (int c = 0; c < 0x108; c++)
                    {
                        byte ch = 0;
                        if (c < pfi.name.Length)
                        {
                            ch = (byte)pfi.name[c];
                        }
                        writer.Write(ch);
                    }
                    writer.Write(pfi.sizeDuplicate);
                    writer.Write(pfi.offset);
                    writer.Write(pfi.modifyTime);
                    writer.Write(pfi.createTime);
                    writer.Write(pfi.dummy2);
                }
                else
                {
                    // Uhm, what now ?
                }

                // encrypt and write our new file into the FAT memory stream
                byte[] decryptedFileData = new byte[bufSize];
                ms.Position = 0;
                ms.Read(decryptedFileData, 0, bufSize);
                byte[] rawFileData = EncryptAES(decryptedFileData, key, true); // encrypt header data
                FAT.Write(rawFileData, 0, bufSize);
            }
            ms.Dispose();

            // Calculate padding to header
            var dif = (FAT.Length % 0x200);

            if (dif > 0)
            {
                var pad = (0x200 - dif);
                FAT.SetLength(FAT.Length + pad);
                FAT.Position = FAT.Length;
            }
            // Update header info
            fileCount      = (uint)_owner.files.Count;
            extraFileCount = (uint)_owner.extraFiles.Count;
            // Stretch size for header
            FAT.SetLength(FAT.Length + headerSize);
            // Encrypt the Header data
            EncryptHeaderData();
            // Write encrypted header
            FAT.Write(rawData, 0, 0x20);

            return(true);
        }
Example #4
0
        /// <summary>
        /// Read and decrypt the File Details Table that was loaded into the FAT MemoryStream
        /// </summary>
        public void ReadFileTable()
        {
            // Check aa.bms QuickBMS file for reference
            FAT.Position = 0;

            int          bufSize = 0x150;                     // Marshal.SizeOf(typeof(AAPakFileInfo));
            MemoryStream ms      = new MemoryStream(bufSize); // Could probably do without the intermediate memorystream, but it's easier to process
            BinaryReader reader  = new BinaryReader(ms);

            // Read the Files
            _owner.files.Clear();
            _owner.extraFiles.Clear();
            var totalFileCount = fileCount + extraFileCount;
            var filesToGo      = fileCount;
            var extraToGo      = extraFileCount;

            for (uint i = 0; i < totalFileCount; i++)
            {
                // Read and decrypt a fileinfo block
                byte[] rawFileData = new byte[bufSize]; // decrypted header data
                FAT.Read(rawFileData, 0, bufSize);
                byte[] decryptedFileData = EncryptAES(rawFileData, key, false);

                // Read decrypted data into a AAPakFileInfo
                ms.SetLength(0);
                ms.Write(decryptedFileData, 0, bufSize);
                ms.Position = 0;
                AAPakFileInfo pfi = new AAPakFileInfo();
                if (_owner.PakType == PakFileType.PakTypeA)
                {
                    // Manually read the string for filename
                    pfi.name = "";
                    for (int c = 0; c < 0x108; c++)
                    {
                        byte ch = reader.ReadByte();
                        if (ch != 0)
                        {
                            pfi.name += (char)ch;
                        }
                        else
                        {
                            break;
                        }
                    }
                    ms.Position       = 0x108;
                    pfi.offset        = reader.ReadInt64();
                    pfi.size          = reader.ReadInt64();
                    pfi.sizeDuplicate = reader.ReadInt64();
                    pfi.paddingSize   = reader.ReadInt32();
                    pfi.md5           = reader.ReadBytes(16);
                    pfi.dummy1        = reader.ReadUInt32(); // observed 0x00000000
                    pfi.createTime    = reader.ReadInt64();
                    pfi.modifyTime    = reader.ReadInt64();
                    pfi.dummy2        = reader.ReadUInt64(); // unused ?
                }
                else
                if (_owner.PakType == PakFileType.PakTypeB)
                {
                    pfi.paddingSize = reader.ReadInt32();
                    pfi.md5         = reader.ReadBytes(16);
                    pfi.dummy1      = reader.ReadUInt32(); // 0x80000000
                    pfi.size        = reader.ReadInt64();
                    // Manually read the string for filename
                    pfi.name = "";
                    for (int c = 0; c < 0x108; c++)
                    {
                        byte ch = reader.ReadByte();
                        if (ch != 0)
                        {
                            pfi.name += (char)ch;
                        }
                        else
                        {
                            break;
                        }
                    }
                    ms.Position       = 0x128;
                    pfi.sizeDuplicate = reader.ReadInt64();
                    pfi.offset        = reader.ReadInt64();
                    pfi.modifyTime    = reader.ReadInt64();
                    pfi.createTime    = reader.ReadInt64();
                    pfi.dummy2        = reader.ReadUInt64(); // unused ?
                }

                if (_owner.PakType == PakFileType.PakTypeA)
                {
                    // TypeA has files first and extra files last
                    if (filesToGo > 0)
                    {
                        filesToGo--;
                        _owner.files.Add(pfi);
                    }
                    else
                    if (extraToGo > 0)
                    {
                        // "Extra" Files. It looks like these are old deleted files renamed to "__unused__"
                        // There might be more to these, but can't be sure at this moment, looks like they are 512 byte blocks on my paks
                        extraToGo--;
                        _owner.extraFiles.Add(pfi);
                    }
                }
                else
                if (_owner.PakType == PakFileType.PakTypeB)
                {
                    // TypeB has extra files first and normal files last
                    if (extraToGo > 0)
                    {
                        extraToGo--;
                        _owner.extraFiles.Add(pfi);
                    }
                    else
                    if (filesToGo > 0)
                    {
                        filesToGo--;
                        _owner.files.Add(pfi);
                    }
                }
                else
                {
                    // Call the police, illegal Types are invading our safespace
                }

                /*
                 * // Debug stuff
                 * if (pfi.name == "bin32/archeage.exe")
                 * {
                 *  ByteArrayToHexFile(decryptedFileData, "file-"+ i.ToString() + ".hex");
                 *  File.WriteAllBytes("file-" + i.ToString() + ".bin",decryptedFileData);
                 * }
                 */

                // Update our "end of file data" location if needed
                if ((pfi.offset + pfi.size + pfi.paddingSize) > AddFileOffset)
                {
                    AddFileOffset = pfi.offset + pfi.size + pfi.paddingSize;
                }
            }

            ms.Dispose();
        }
Example #5
0
        /// <summary>
        /// Adds a new file into the pak
        /// </summary>
        /// <param name="filename">Filename of the file inside the pakfile</param>
        /// <param name="sourceStream">Source Stream containing the file data</param>
        /// <param name="CreateTime">Time to use as initial file creation timestamp</param>
        /// <param name="ModifyTime">Time to use as last modified timestamp</param>
        /// <param name="autoSpareSpace">When set, tries to pre-allocate extra free space at the end of the file, this will be 25% of the filesize if used. If a "deleted file" is used, this parameter is ignored</param>
        /// <param name="pfi">Returns the fileinfo of the newly created file</param>
        /// <returns>Returns true on success</returns>
        public bool AddAsNewFile(string filename, Stream sourceStream, DateTime CreateTime, DateTime ModifyTime, bool autoSpareSpace, out AAPakFileInfo pfi)
        {
            // When we have a new file, or previous space wasn't enough, we will add it where the file table starts, and move the file table
            if (readOnly)
            {
                pfi = nullAAPakFileInfo;
                return(false);
            }
            bool addedAtTheEnd = true;

            AAPakFileInfo newFile = new AAPakFileInfo();

            newFile.name          = filename;
            newFile.offset        = _header.FirstFileInfoOffset;
            newFile.size          = sourceStream.Length;
            newFile.sizeDuplicate = newFile.size;
            newFile.createTime    = CreateTime.ToFileTime();
            newFile.modifyTime    = ModifyTime.ToFileTime();
            newFile.paddingSize   = 0;
            newFile.md5           = new byte[16];
            if (PakType == PakFileType.PakTypeB)
            {
                newFile.dummy1 = 0x80000000;
            }

            // check if we have "unused" space in extraFiles that we can use
            for (int i = 0; i < extraFiles.Count; i++)
            {
                if (newFile.size <= extraFiles[i].size)
                {
                    // Copy the spare file's properties and remove it from extraFiles
                    newFile.offset      = extraFiles[i].offset;
                    newFile.paddingSize = (int)(extraFiles[i].size - newFile.size); // This should already be aligned
                    addedAtTheEnd       = false;
                    extraFiles.Remove(extraFiles[i]);
                    break;
                }
            }

            if (addedAtTheEnd)
            {
                // Only need to calculate padding if we are adding at the end
                var dif = (newFile.size % 0x200);
                if (dif > 0)
                {
                    newFile.paddingSize = (int)(0x200 - dif);
                }
                if (autoSpareSpace)
                {
                    // If autoSpareSpace is used to add files, we will reserve some extra space as padding
                    // Add 25% by default
                    var spareSpace = (newFile.size / 4);
                    spareSpace          -= (spareSpace % 0x200); // Align the spare space
                    newFile.paddingSize += (int)spareSpace;
                }
            }

            // Add to files list
            files.Add(newFile);

            isDirty = true;

            // Add File Data
            _gpFileStream.Position = newFile.offset;
            sourceStream.Position  = 0;
            sourceStream.CopyTo(_gpFileStream);

            if (addedAtTheEnd)
            {
                _header.FirstFileInfoOffset = newFile.offset + newFile.size + newFile.paddingSize;
            }

            UpdateMD5(newFile); // TODO: optimize this to calculate WHILE we are copying the stream

            // Set output
            pfi = newFile;
            return(true);
        }