Example #1
0
        private Afs2Archive GetExternalAwbArchive()
        {
            var acbFileName = AcbFileName;
            var awbDirPath  = Path.GetDirectoryName(acbFileName);

            if (awbDirPath == null)
            {
                awbDirPath = string.Empty;
            }

            var awbBaseFileName = Path.GetFileNameWithoutExtension(acbFileName);

            string[] awbFiles = null;
            string   awbMask1 = null, awbMask2 = null, awbMask3 = null;

            if (awbFiles == null || awbFiles.Length < 1)
            {
                awbMask1 = string.Format(AwbFileNameFormats.Format1, awbBaseFileName);
                awbFiles = Directory.GetFiles(awbDirPath, awbMask1, SearchOption.TopDirectoryOnly);
            }

            if (awbFiles == null || awbFiles.Length < 1)
            {
                awbMask2 = string.Format(AwbFileNameFormats.Format2, awbBaseFileName);
                awbFiles = Directory.GetFiles(awbDirPath, awbMask2, SearchOption.TopDirectoryOnly);
            }

            if (awbFiles == null || awbFiles.Length < 1)
            {
                awbMask3 = string.Format(AwbFileNameFormats.Format3, awbBaseFileName);
                awbFiles = Directory.GetFiles(awbDirPath, awbMask3, SearchOption.TopDirectoryOnly);
            }

            if (awbFiles.Length < 1)
            {
                throw new FileNotFoundException($"Cannot find AWB file. Please verify corresponding AWB file is named '{awbMask1}', '{awbMask2}', or '{awbMask3}'.");
            }

            if (awbFiles.Length > 1)
            {
                throw new FileNotFoundException($"More than one matching AWB file for this ACB. Please verify only one AWB file is named '{awbMask1}', '{awbMask2}' or '{awbMask3}'.");
            }

            var externalAwbHash = GetFieldValueAsData(0, "StreamAwbHash");
            var fs      = File.Open(awbFiles[0], FileMode.Open, FileAccess.Read, FileShare.Read);
            var awbHash = AcbHelper.GetMd5Checksum(fs);

            if (!AcbHelper.AreDataIdentical(awbHash, externalAwbHash))
            {
                Trace.WriteLine($"Checksum of AWB file '{fs.Name}' ({BitConverter.ToString(awbHash)}) does not match MD5 checksum inside ACB file '{acbFileName}' ({BitConverter.ToString(externalAwbHash)}).");
            }

            var archive = new Afs2Archive(fs, 0, fs.Name, true);

            archive.Initialize();

            return(archive);
        }
Example #2
0
        private Stream GetDataStreamFromCueInfo(AcbCueRecord cue, string fileNameForErrorInfo)
        {
            if (!cue.IsWaveformIdentified)
            {
                throw new InvalidOperationException($"File '{fileNameForErrorInfo}' is not identified.");
            }

            Stream result;

            if (cue.IsStreaming)
            {
                var externalAwb = ExternalAwb;
                if (externalAwb == null)
                {
                    throw new InvalidOperationException($"External AWB does not exist for streaming file '{fileNameForErrorInfo}'.");
                }

                if (!externalAwb.Files.ContainsKey(cue.WaveformId))
                {
                    throw new InvalidOperationException($"Waveform ID {cue.WaveformId} is not found in AWB file {externalAwb.FileName}.");
                }

                var targetExternalFile = externalAwb.Files[cue.WaveformId];

                using (var fs = File.Open(externalAwb.FileName, FileMode.Open, FileAccess.Read, FileShare.Read)) {
                    result = AcbHelper.ExtractToNewStream(fs, targetExternalFile.FileOffsetAligned, (int)targetExternalFile.FileLength);
                }
            }
            else
            {
                var internalAwb = InternalAwb;

                if (internalAwb == null)
                {
                    throw new InvalidOperationException($"Internal AWB is not found for memory file '{fileNameForErrorInfo}' in '{AcbFileName}'.");
                }

                if (!internalAwb.Files.ContainsKey(cue.WaveformId))
                {
                    throw new InvalidOperationException($"Waveform ID {cue.WaveformId} is not found in internal AWB in {AcbFileName}.");
                }

                var targetInternalFile = internalAwb.Files[cue.WaveformId];

                result = AcbHelper.ExtractToNewStream(Stream, targetInternalFile.FileOffsetAligned, (int)targetInternalFile.FileLength);
            }

            return(result);
        }
Example #3
0
        public Stream OpenDataStream(string fileName)
        {
            AcbCueRecord cue;

            try {
                cue = Cues.Single(c => c.CueName == fileName);
            } catch (InvalidOperationException ex) {
                throw new InvalidOperationException($"File '{fileName}' is not found or it has multiple entries.", ex);
            }
            if (!cue.IsWaveformIdentified)
            {
                throw new InvalidOperationException($"File '{fileName}' is not identified.");
            }
            if (cue.IsStreaming)
            {
                var externalAwb = ExternalAwb;
                if (externalAwb == null)
                {
                    throw new InvalidOperationException($"External AWB does not exist for streaming file '{fileName}'.");
                }
                if (!externalAwb.Files.ContainsKey(cue.WaveformId))
                {
                    throw new InvalidOperationException($"Waveform ID {cue.WaveformId} is not found in AWB file {externalAwb.FileName}.");
                }
                var targetExternalFile = externalAwb.Files[cue.WaveformId];
                using (var fs = File.Open(externalAwb.FileName, FileMode.Open, FileAccess.Read)) {
                    return(AcbHelper.ExtractToNewStream(fs, targetExternalFile.FileOffsetAligned, (int)targetExternalFile.FileLength));
                }
            }
            else
            {
                var internalAwb = InternalAwb;
                if (internalAwb == null)
                {
                    throw new InvalidOperationException($"Internal AWB is not found for memory file '{fileName}' in '{AcbFileName}'.");
                }
                if (!internalAwb.Files.ContainsKey(cue.WaveformId))
                {
                    throw new InvalidOperationException($"Waveform ID {cue.WaveformId} is not found in internal AWB in {AcbFileName}.");
                }
                var targetInternalFile = internalAwb.Files[cue.WaveformId];
                return(AcbHelper.ExtractToNewStream(Stream, targetInternalFile.FileOffsetAligned, (int)targetInternalFile.FileLength));
            }
        }
Example #4
0
        internal virtual void Initialize()
        {
            var stream = _stream;
            var offset = _offset;

            var magic = stream.PeekBytes(offset, 4);

            magic = CheckEncryption(magic);
            if (!AcbHelper.AreDataIdentical(magic, UtfSignature))
            {
                throw new FormatException($"'@UTF' signature (or its encrypted equivalent) is not found in '{_acbFileName}' at offset 0x{offset:x8}.");
            }
            using (var tableDataStream = GetTableDataStream()) {
                var header = GetUtfHeader(tableDataStream);
                _utfHeader = header;
                _rows      = new Dictionary <string, UtfField> [header.RowCount];
                if (header.TableSize > 0)
                {
                    InitializeUtfSchema(stream, tableDataStream, 0x20);
                }
            }
        }
Example #5
0
 private byte[] CheckEncryption(byte[] magicBytes)
 {
     if (AcbHelper.AreDataIdentical(magicBytes, UtfSignature))
     {
         _isEncrypted = false;
         _utfReader   = new UtfReader();
         return(magicBytes);
     }
     else
     {
         _isEncrypted = true;
         byte seed, increment;
         var  keysFound = GetKeysForEncryptedUtfTable(magicBytes, out seed, out increment);
         if (!keysFound)
         {
             throw new FormatException($"Unable to decrypt UTF table at offset 0x{_offset:x8}");
         }
         else
         {
             _utfReader = new UtfReader(seed, increment);
         }
         return(UtfSignature);
     }
 }
Example #6
0
        public static bool IsAfs2Archive(Stream stream, long offset)
        {
            var fileSignature = stream.PeekBytes(offset, 4);

            return(AcbHelper.AreDataIdentical(fileSignature, Afs2Signature));
        }
Example #7
0
        public void Initialize()
        {
            var stream      = _stream;
            var offset      = _streamOffset;
            var acbFileName = _fileName;

            if (!IsAfs2Archive(stream, offset))
            {
                throw new FormatException($"File '{acbFileName}' does not contain a valid AFS2 archive at offset {offset}.");
            }
            var fileCount = (int)stream.PeekUInt32LE(offset + 8);

            if (fileCount > ushort.MaxValue)
            {
                throw new IndexOutOfRangeException($"File count {fileCount} exceeds maximum possible value (65535).");
            }
            var files = new Dictionary <int, Afs2FileRecord>(fileCount);

            _files = files;
            var byteAlignment = stream.PeekUInt32LE(offset + 12);

            _byteAlignment  = byteAlignment & 0xffff; // Is the mask a constant?
            _hcaKeyModifier = (ushort)(byteAlignment >> 16);
            var version = stream.PeekUInt32LE(offset + 4);

            _version = version;
            var  offsetFieldSize = (int)(version >> 8) & 0xff; // versionBytes[1], always 4?
            uint offsetMask      = 0;

            for (var j = 0; j < offsetFieldSize; j++)
            {
                offsetMask |= (uint)(0xff << (j * 8));
            }

            const int invalidCueId        = -1;
            var       previousCueId       = invalidCueId;
            var       fileOffsetFieldBase = 0x10 + fileCount * 2;

            for (ushort i = 0; i < fileCount; ++i)
            {
                var currentFileOffsetBase = fileOffsetFieldBase + offsetFieldSize * i;
                var record = new Afs2FileRecord {
                    CueId = stream.PeekUInt16LE(offset + (0x10 + 2 * i)),
                    // TODO: Dynamically judge if the field is U32/U16 or else (see offsetFieldSize).
                    FileOffsetRaw = stream.PeekUInt32LE(offset + currentFileOffsetBase)
                };
                record.FileOffsetRaw    &= offsetMask;
                record.FileOffsetRaw    += offset;
                record.FileOffsetAligned = AcbHelper.RoundUpToAlignment(record.FileOffsetRaw, ByteAlignment);
                if (i == fileCount - 1)
                {
                    record.FileLength = stream.PeekUInt32LE(offset + currentFileOffsetBase + offsetFieldSize) + offset - record.FileOffsetAligned;
                }
                if (previousCueId != invalidCueId)
                {
                    files[previousCueId].FileLength = record.FileOffsetRaw - files[previousCueId].FileOffsetAligned;
                }
                files.Add(record.CueId, record);
                previousCueId = record.CueId;
            }
        }