public static DuplicatableStream ReadDuplicatableSubstreamFromLocationAndReset(this Stream stream, long position, long bytecount)
        {
            long p = stream.Position;
            DuplicatableStream?ds = stream as DuplicatableStream;

            if (ds != null)
            {
                return(new PartialStream(ds, p, bytecount));
            }
            else
            {
                stream.Position = position;
                DuplicatableStream nds = new DuplicatableByteArrayStream(ReadBytes(stream, bytecount));
                stream.Position = p;
                return(nds);
            }
        }
Example #2
0
        private static void ReplaceFile(HyoutaTools.Tales.CPK.CpkFile file, DuplicatableByteArrayStream stream, bool tryCompress = false)
        {
            if (tryCompress && stream.Length > 0x100)
            {
                MemoryStream ms  = new MemoryStream();
                long         len = utf_tab_sharp.CpkCompress.compress(stream, 0, stream.Length, ms);
                if (len < stream.Length)
                {
                    file.FileStream       = ms.CopyToByteArrayStreamAndDispose();
                    file.DecompressedSize = (uint)stream.Length;
                    return;
                }
            }

            file.FileStream       = stream;
            file.DecompressedSize = (uint)stream.Length;
        }
Example #3
0
        public t_voice_tbl(Stream stream)
        {
            Stream s = new DuplicatableByteArrayStream(stream.CopyToByteArray());

            EndianUtils.Endianness e = EndianUtils.Endianness.LittleEndian;
            ushort count             = s.ReadUInt16(e);

            if (s.PeekUInt32(e) == 1)
            {
                // CS2 has something odd I don't fully understand at the start of the file, try to skip past that
                s.Position += 0xe;
            }

            Entries = new List <t_voice_tbl_entry>();
            for (int i = 0; i < count; ++i)
            {
                Entries.Add(new t_voice_tbl_entry(s, e));
            }

            return;
        }
Example #4
0
        private static ulong PackDataBlock(Stream target, List <HyoutaArchiveFileInfo> files, byte packedAlignment, EndianUtils.Endianness endian)
        {
            byte smallPackedAlignment = ToSmallPackedAlignment(packedAlignment);

            long startPosition = target.Position;

            target.WriteUInt16(0);             // offsetToFirstFileInfo, fill in later

            bool hasDummyContent    = files.Any(x => x.DummyContent != null);
            uint dummyContentLength = hasDummyContent ? ((uint)files.Max(x => x.DummyContent?.Length ?? 0)).Align(1 << smallPackedAlignment) : 0;
            bool hasFilename        = files.Any(x => x.Filename != null);
            uint filenameLength     = 0;
            //bool embedFilenamesInFileInfo = false;
            List <byte[]?>?encodedFilenames = null;

            if (hasFilename)
            {
                // figure out whether we want the strings to embed into the fileinfo directly
                // or whether to use an offset and write the string data at the end of the fileinfo
                // note that if a string is <= 8 bytes we can always embed it as we'd need 8 bytes for the offset anyway
                // so...
                encodedFilenames = new List <byte[]?>(files.Count);
                long longestBytecount        = 0;
                long totalBytecount          = 0;
                long filenameCountOver8Bytes = 0;
                for (int i = 0; i < files.Count; ++i)
                {
                    var currentFilename = files[i].Filename;
                    if (currentFilename == null)
                    {
                        encodedFilenames.Add(null);
                    }
                    else
                    {
                        byte[] stringbytes = EncodeString(currentFilename);
                        encodedFilenames.Add(stringbytes);

                        if (stringbytes.LongLength > 8)
                        {
                            longestBytecount = Math.Max(longestBytecount, stringbytes.LongLength);
                            totalBytecount  += stringbytes.LongLength;
                            ++filenameCountOver8Bytes;
                        }
                    }
                }

                // alright so we have, in practice, two options here
                // - make filenameLength == 16, store strings that are longer than that offsetted
                long nonEmbedSize = files.Count * 16 + totalBytecount.Align(1 << smallPackedAlignment);
                // - make filenameLength long enough so all strings can be embedded
                long embedSize = files.Count * (8 + longestBytecount).Align(1 << smallPackedAlignment);

                // pick whatever results in a smaller file; on a tie embed
                if (nonEmbedSize < embedSize)
                {
                    //embedFilenamesInFileInfo = false;
                    filenameLength = 16;
                }
                else
                {
                    //embedFilenamesInFileInfo = true;
                    filenameLength = (uint)(8 + longestBytecount).Align(1 << smallPackedAlignment);
                }
            }
            bool hasCompression        = files.Any(x => x.CompressionInfo != null);
            uint compressionInfoLength = hasCompression ? files.Max(x => x.CompressionInfo?.MaximumCompressionInfoLength() ?? 0).Align(1 << smallPackedAlignment) : 0;
            bool hasBpsPatch           = files.Any(x => x.BpsPatchInfo != null);
            uint bpsPatchInfoLength    = hasBpsPatch ? 16u.Align(1 << smallPackedAlignment) : 0;
            bool hasCrc32           = files.Any(x => x.crc32 != null);
            uint crc32ContentLength = hasCrc32 ? 4u.Align(1 << smallPackedAlignment) : 0u;
            bool hasMd5             = files.Any(x => x.md5 != null);
            uint md5ContentLength   = hasMd5 ? 16u.Align(1 << smallPackedAlignment) : 0u;
            bool hasSha1            = files.Any(x => x.sha1 != null);
            uint sha1ContentLength  = hasSha1 ? 20u.Align(1 << smallPackedAlignment) : 0u;

            ushort contentBitfield1 = 0;

            contentBitfield1 |= (ushort)(hasDummyContent ? 0x0001u : 0);
            contentBitfield1 |= (ushort)(hasFilename ? 0x0002u : 0);
            contentBitfield1 |= (ushort)(hasCompression ? 0x0004u : 0);
            contentBitfield1 |= (ushort)(hasBpsPatch ? 0x0008u : 0);
            contentBitfield1 |= (ushort)(hasCrc32 ? 0x0010u : 0);
            contentBitfield1 |= (ushort)(hasMd5 ? 0x0020u : 0);
            contentBitfield1 |= (ushort)(hasSha1 ? 0x0040u : 0);
            target.WriteUInt16(contentBitfield1, endian);

            if (hasDummyContent)
            {
                WriteContentLength(dummyContentLength, target, endian);
            }
            if (hasFilename)
            {
                WriteContentLength(filenameLength, target, endian);
            }
            if (hasCompression)
            {
                WriteContentLength(compressionInfoLength, target, endian);
            }
            if (hasBpsPatch)
            {
                WriteContentLength(bpsPatchInfoLength, target, endian);
            }
            if (hasCrc32)
            {
                WriteContentLength(crc32ContentLength, target, endian);
            }
            if (hasMd5)
            {
                WriteContentLength(md5ContentLength, target, endian);
            }
            if (hasSha1)
            {
                WriteContentLength(sha1ContentLength, target, endian);
            }

            long offsetToFirstFileInfo = (target.Position - startPosition).Align(1 << smallPackedAlignment);

            StreamUtils.WriteZeros(target, offsetToFirstFileInfo - (target.Position - startPosition));
            target.Position = startPosition;
            WriteContentLength((uint)offsetToFirstFileInfo, target, endian);
            target.Position = startPosition + offsetToFirstFileInfo;

            long singleFileInfoLength  = 16 + dummyContentLength + filenameLength + compressionInfoLength + bpsPatchInfoLength + crc32ContentLength + md5ContentLength + sha1ContentLength;
            long totalFileInfoLength   = singleFileInfoLength * files.Count;
            long offsetToEndOfFileInfo = (offsetToFirstFileInfo + totalFileInfoLength).Align(1 << smallPackedAlignment);

            StreamUtils.WriteZeros(target, offsetToEndOfFileInfo - offsetToFirstFileInfo);

            var filedata = new List <(long position, DuplicatableStream data)>(files.Count);

            long positionOfFreeSpace = offsetToEndOfFileInfo;

            for (int i = 0; i < files.Count; ++i)
            {
                HyoutaArchiveFileInfo fi = files[i];
                var fiData = fi.Data;
                if (fiData == null)
                {
                    throw new Exception("Data of file " + i + " is null.");
                }
                using (DuplicatableStream fs = fiData.Duplicate()) {
                    DuplicatableStream streamToWrite = fs;

                    bool streamIsInternallyCompressed = fi.StreamIsCompressed;
                    if (fi.BpsPatchInfo != null && fi.CompressionInfo != null && streamIsInternallyCompressed && !fi.StreamIsBpsPatch)
                    {
                        // this is a weird case; the stream wants both bps patch and compression
                        // and is already compressed but not already bps patched, which breaks the defined order
                        // we can handle this by decompressing, creating patch, recompressing
                        streamToWrite = fi.DataStream.Duplicate();                    // this decompresses the stream
                        streamIsInternallyCompressed = false;                         // and fake-set the stream as uncompressed for packing logic
                    }

                    byte[]? bpsPatchInfoBytes    = null;
                    byte[]? compressionInfoBytes = null;
                    if (hasBpsPatch)
                    {
                        if (fi.BpsPatchInfo == null)
                        {
                            // chunk has patches but this file is unpatched; we store this by pointing the file to itself
                            bpsPatchInfoBytes = new HyoutaArchiveBpsPatchInfo((ulong)i, (ulong)streamToWrite.Length, null).Serialize(endian);
                        }
                        else if (fi.StreamIsBpsPatch)
                        {
                            bpsPatchInfoBytes = fi.BpsPatchInfo.Serialize(endian);
                        }
                        else
                        {
                            var p = HyoutaArchiveBps.CreatePatch(fi.BpsPatchInfo, streamToWrite, endian);
                            bpsPatchInfoBytes = p.patchInfo;
                            streamToWrite     = new DuplicatableByteArrayStream(p.patchData);
                        }
                    }
                    if (hasCompression && fi.CompressionInfo != null)
                    {
                        if (streamIsInternallyCompressed)
                        {
                            compressionInfoBytes = fi.CompressionInfo.Serialize(endian);
                        }
                        else
                        {
                            var p = fi.CompressionInfo.Compress(streamToWrite, endian);
                            compressionInfoBytes = p.compressionInfo;
                            streamToWrite        = new DuplicatableByteArrayStream(p.compressedData);
                        }
                    }

                    // write file info
                    target.Position = (singleFileInfoLength * i) + offsetToFirstFileInfo + startPosition;
                    long positionPosition = target.Position;
                    target.WriteUInt64(0);                     // position of file, will be filled later
                    target.WriteUInt64((ulong)streamToWrite.Length, endian);
                    if (hasDummyContent)
                    {
                        if (fi.DummyContent != null)
                        {
                            target.Write(fi.DummyContent);
                            target.WriteZeros(dummyContentLength - fi.DummyContent.Length);
                        }
                        else
                        {
                            target.WriteZeros(dummyContentLength);
                        }
                    }
                    if (hasFilename)
                    {
                        if (fi.Filename != null)
                        {
                            var efn = encodedFilenames ![i];
Example #5
0
        public static List <MemChunk> FindFreeMemoryInFontTexture(MemoryStream fontStream)
        {
            DuplicatableStream textureWiiStream = new DuplicatableByteArrayStream(fontStream.CopyToByteArray());

            HyoutaTools.Tales.Vesperia.FPS4.FPS4   textureWiiFps4 = new HyoutaTools.Tales.Vesperia.FPS4.FPS4(textureWiiStream);
            HyoutaTools.Tales.Vesperia.Texture.TXM textureWiiTxm  = new HyoutaTools.Tales.Vesperia.Texture.TXM(textureWiiFps4.GetChildByIndex(0).AsFile.DataStream);
            HyoutaTools.Tales.Vesperia.Texture.TXV textureWiiTxv  = new HyoutaTools.Tales.Vesperia.Texture.TXV(textureWiiTxm, textureWiiFps4.GetChildByIndex(1).AsFile.DataStream, false);
            Bitmap bitmapWii = textureWiiTxv.textures[2].GetBitmaps()[0];
            {
                for (int y = 0; y < bitmapWii.Height; ++y)
                {
                    for (int x = 0; x < bitmapWii.Width; ++x)
                    {
                        Color color;
                        switch (IdentifyPixel(x, y))
                        {
                        case TileIdentification.UsedTile:
                            color = Color.FromArgb(0, 255, 0);
                            break;

                        case TileIdentification.UnusedTile:
                            color = Color.FromArgb(0, 0, 255);
                            break;

                        default:
                            throw new Exception("???");
                        }
                        bitmapWii.SetPixel(x, y, color);
                    }
                }
            }

            var          pxit    = new HyoutaTools.Textures.PixelOrderIterators.TiledPixelOrderIterator(bitmapWii.Width, bitmapWii.Height, 8, 8);
            MemoryStream stream  = new MemoryStream();
            byte         storage = 0;
            bool         even    = false;

            foreach (var px in pxit)
            {
                if (px.X < bitmapWii.Width && px.Y < bitmapWii.Height)
                {
                    Color col         = bitmapWii.GetPixel(px.X, px.Y);
                    bool  pixelUnused = col.B > 0;
                    var   colidx      = pixelUnused ? 0xF : 0x0;
                    if (!even)
                    {
                        storage = (byte)colidx;
                    }
                    else
                    {
                        storage = (byte)(storage << 4 | (byte)colidx);
                        stream.WriteByte(storage == 0xFF ? (byte)1 : (byte)0);
                    }
                    even = !even;
                }
            }

            List <MemChunk> chunks = new List <MemChunk>();
            uint            offset = textureWiiFps4.Files[1].Location.Value + textureWiiTxv.textures[2].TXM.TxvLocation;
            long            len    = stream.Length;
            long            startOfLastSafeBlock = -1;
            var             fontMapper           = new FontMapper(0xE0000000 - textureWiiFps4.Files[1].Location.Value);

            stream.Position = 0;
            for (long i = 0; i <= len; ++i)
            {
                bool safeToWriteTo = i == len ? false : stream.ReadUInt8() == 1;
                if (safeToWriteTo && startOfLastSafeBlock == -1)
                {
                    // start of block
                    startOfLastSafeBlock = i;
                }
                else if (!safeToWriteTo && startOfLastSafeBlock != -1)
                {
                    // end of block
                    long     start  = startOfLastSafeBlock;
                    long     length = i - startOfLastSafeBlock;
                    MemChunk mc     = new MemChunk();
                    mc.Address    = (uint)(start + offset);
                    mc.FreeBytes  = (uint)length;
                    mc.File       = fontStream;
                    mc.Mapper     = fontMapper;
                    mc.IsInternal = false;
                    chunks.Add(mc);
                    startOfLastSafeBlock = -1;
                }
            }

            return(chunks);
        }
Example #6
0
        internal static Stream InjectEnglishContainedVoice(Config config, FileFetcher _fc, string name, DuplicatableStream wstream, DuplicatableStream jstream, DuplicatableStream ustream, ContainedVoiceInfo cvi, SkitTexCache skitTexCache)
        {
            var fps4         = new HyoutaTools.Tales.Vesperia.FPS4.FPS4(wstream.Duplicate());
            var se3stream    = fps4.GetChildByIndex(cvi.SE3Index).AsFile.DataStream;
            var se3          = new HyoutaTools.Tales.Vesperia.SE3.SE3(se3stream.Duplicate(), EndianUtils.Endianness.BigEndian, TextUtils.GameTextEncoding.ASCII);
            var se3ms        = se3.ExtractSe3HeaderStream();
            var nubms        = se3.ExtractNubStream();
            var nubstream    = new DuplicatableByteArrayStream(nubms.CopyToByteArrayAndDispose());
            var newnubstream = RebuildNubStream(nubstream, Path.Combine(config.EnglishVoiceProcessingDir, "other"), cvi.WiiType, x => Path.GetFileNameWithoutExtension(name));
            var newse3stream = new MemoryStream();

            se3ms.Position        = 0;
            newnubstream.Position = 0;
            StreamUtils.CopyStream(se3ms, newse3stream);
            StreamUtils.CopyStream(newnubstream, newse3stream);

            if (cvi.IsSkit)
            {
                using (var texIdStream = fps4.GetChildByIndex(3).AsFile.DataStream.Duplicate().CopyToByteArrayStreamAndDispose()) {
                    int    idx    = 4;
                    uint[] texIds = texIdStream.ReadUInt32Array(texIdStream.Length / 4, EndianUtils.Endianness.BigEndian);
                    foreach (uint texId in texIds)
                    {
                        skitTexCache.AddTextureIfNotExists(texId, fps4.GetChildByIndex(idx).AsFile.DataStream.Duplicate().CopyToByteArrayStreamAndDispose());
                        ++idx;
                    }
                }
            }

            newse3stream.Position = 0;
            MemoryStream newfps4stream = new MemoryStream();

            using (var ufps4 = new HyoutaTools.Tales.Vesperia.FPS4.FPS4(ustream.Duplicate())) {
                uint[] utexIds = null;
                if (cvi.IsSkit)
                {
                    using (var texIdStream = ufps4.GetChildByIndex(3).AsFile.DataStream.Duplicate().CopyToByteArrayStreamAndDispose()) {
                        utexIds = texIdStream.ReadUInt32Array(texIdStream.Length / 4, EndianUtils.Endianness.BigEndian);
                    }
                }

                List <HyoutaTools.Tales.Vesperia.FPS4.PackFileInfo> packFileInfos = new List <HyoutaTools.Tales.Vesperia.FPS4.PackFileInfo>(fps4.Files.Count);
                for (int i = 0; i < (cvi.IsSkit ? ufps4 : fps4).Files.Count - 1; ++i)
                {
                    var pf = new HyoutaTools.Tales.Vesperia.FPS4.PackFileInfo();
                    pf.Name = (cvi.IsSkit ? ufps4 : fps4).Files[i].FileName;
                    if (i == cvi.SE3Index)
                    {
                        pf.DataStream = new DuplicatableByteArrayStream(newse3stream.CopyToByteArrayAndDispose());
                    }
                    else if (cvi.IsSkit && (i == 0 || i == 2 || i == 3))
                    {
                        // copy over the actual skit script/timing from the EN version so the voice timing and lipsync matches with the skit
                        pf.DataStream = ufps4.GetChildByIndex(i).AsFile.DataStream.Duplicate();
                    }
                    else if (cvi.IsSkit && i >= 4)
                    {
                        uint texId = NormalizePs3SkitTextureIdForWii(utexIds[i - 4]);
                        try {
                            pf.DataStream = skitTexCache.GetTextureStream(texId);
                        } catch (Exception ex) {
                            Console.WriteLine("ERROR: Failed to get skit texture with ID 0x" + texId.ToString("x4"));
                            Console.WriteLine("       tex name: " + new SkitTexCache.SkitTex()
                            {
                                Stream = ufps4.GetChildByIndex(i).AsFile.DataStream.Duplicate()
                            }.ToString());
                            throw ex;
                        }
                    }
                    else
                    {
                        pf.DataStream = fps4.GetChildByIndex(i).AsFile.DataStream.Duplicate();
                    }
                    pf.Length = pf.DataStream.Length;
                    packFileInfos.Add(pf);
                }
                packFileInfos = HyoutaTools.Tales.Vesperia.FPS4.FPS4.DetectDuplicates(packFileInfos);
                HyoutaTools.Tales.Vesperia.FPS4.FPS4.Pack(packFileInfos, newfps4stream, fps4.ContentBitmask, EndianUtils.Endianness.BigEndian, fps4.Unknown2, cvi.IsSkit ? null : wstream.Duplicate(), fps4.ArchiveName, fps4.FirstFileStart, 0x20);
            }

            //using (var fs = new FileStream(Path.Combine(@"c:\__graces\______fps4repacktest\", name.Replace("/", "_") + "_old.fps4"), FileMode.Create)) {
            //	using (var wcpy = wstream.Duplicate()) {
            //		wcpy.Position = 0;
            //		StreamUtils.CopyStream(wcpy, fs);
            //	}
            //}
            //using (var fs = new FileStream(Path.Combine(@"c:\__graces\______fps4repacktest\", name.Replace("/", "_") + "_new.fps4"), FileMode.Create)) {
            //	newfps4stream.Position = 0;
            //	StreamUtils.CopyStream(newfps4stream, fs);
            //}

            newfps4stream.Position = 0;
            return(newfps4stream);
        }