public void AddTextureIfNotExists(uint key, DuplicatableStream texture) { SkitTex containedTexture; if (Cache.TryGetValue(key, out containedTexture)) { const bool verify = false; if (verify) { using (DuplicatableStream otex = containedTexture.Stream.Duplicate()) using (DuplicatableStream ntex = texture.Duplicate()) { otex.Position = 0; ntex.Position = 0; if (!(otex.Length == ntex.Length && StreamUtils.IsIdentical(otex, ntex, otex.Length))) { throw new Exception("texture added does not match known texture"); } } } } else { Cache.Add(key, new SkitTex() { Stream = texture.Duplicate() }); } }
public PartialStream(DuplicatableStream stream, long position, long length) { if (position < 0) { throw new Exception("Invalid position, must be positive."); } if (length < 0) { throw new Exception("Invalid length, must be positive."); } if (stream is PartialStream) { // optimization to better chain partial stream of partial stream PartialStream parent = stream as PartialStream; BaseStreamInternal = parent.BaseStreamInternal.Duplicate(); Initialized = false; PartialStart = parent.PartialStart + position; PartialLength = length; CurrentPosition = 0; } else { BaseStreamInternal = stream.Duplicate(); Initialized = false; PartialStart = position; PartialLength = length; CurrentPosition = 0; } }
private bool LoadFile(DuplicatableStream headerStream, DuplicatableStream contentStream) { DuplicatableStream infile = headerStream.Duplicate(); contentFile = contentStream.Duplicate(); infile.Seek(0x00, SeekOrigin.Begin); string magic = infile.ReadAscii(4); if (magic != "FPS4") { Console.WriteLine("Not an FPS4 file!"); return(false); } Endian = Util.Endianness.BigEndian; FileCount = infile.ReadUInt32().FromEndian(Endian); HeaderSize = infile.ReadUInt32().FromEndian(Endian); // if header seems huge then we probably have assumed the wrong endianness if (HeaderSize > 0xFFFF) { Endian = Util.Endianness.LittleEndian; FileCount = FileCount.ToEndian(Util.Endianness.BigEndian).FromEndian(Endian); HeaderSize = HeaderSize.ToEndian(Util.Endianness.BigEndian).FromEndian(Endian); } FirstFileStart = infile.ReadUInt32().FromEndian(Endian); EntrySize = infile.ReadUInt16().FromEndian(Endian); ContentBitmask = new ContentInfo(infile.ReadUInt16().FromEndian(Endian)); Unknown2 = infile.ReadUInt32().FromEndian(Endian); ArchiveNameLocation = infile.ReadUInt32().FromEndian(Endian); infile.Position = ArchiveNameLocation; if (ArchiveNameLocation > 0) { ArchiveName = infile.ReadShiftJisNullterm(); } Alignment = FirstFileStart; Console.WriteLine("Content Bitmask: 0x" + ContentBitmask.Value.ToString("X4")); if (ContentBitmask.HasUnknownDataTypes) { Console.WriteLine("WARNING: Bitmask identifies unknown data types, data interpretation will probably be incorrect."); } Files = new List <FileInfo>((int)FileCount); for (uint i = 0; i < FileCount; ++i) { infile.Position = HeaderSize + (i * EntrySize); Files.Add(new FileInfo(infile, i, ContentBitmask, Endian, Util.GameTextEncoding.ASCII)); } FileLocationMultiplier = CalculateFileLocationMultiplier(); ShouldGuessFilesizeFromNextFile = !ContentBitmask.ContainsFileSizes && !ContentBitmask.ContainsSectorSizes && CalculateIsLinear(); infile.Dispose(); return(true); }
public NUB(DuplicatableStream duplicatableStream, EndianUtils.Endianness?e) { Stream = duplicatableStream.Duplicate(); Stream.Position = 0; Header = new NubHeader(Stream, e); Endian = Header.Endian; Stream.Position = Header.StartOfEntries; Entries = Stream.ReadUInt32Array(Header.EntryCount, Endian); }
public override string ToString() { using (FPS4 fps4 = new FPS4(Stream.Duplicate())) using (DuplicatableStream txmvStream = fps4.GetChildByIndex(1).AsFile.DataStream.Duplicate()) using (FPS4 txmv = new FPS4(txmvStream.Duplicate())) using (DuplicatableStream txmStream = txmv.GetChildByIndex(0).AsFile.DataStream.Duplicate()) { TXM txm = new TXM(txmStream.Duplicate()); return(txm.TXMRegulars[0].Name); } }
private bool LoadFile(DuplicatableStream inputStream, EndianUtils.Endianness?endianParam, TextUtils.GameTextEncoding encoding) { DuplicatableStream stream = inputStream.Duplicate(); stream.Position = 0; EndianUtils.Endianness endian; if (endianParam == null) { uint magic = stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian); if (magic == 0x53453320) { endian = EndianUtils.Endianness.BigEndian; } else if (magic == 0x20334553) { endian = EndianUtils.Endianness.LittleEndian; } else { Console.WriteLine("Invalid magic: " + magic); return(false); } } else { endian = endianParam.Value; uint magic = stream.ReadUInt32(endian); if (magic != 0x53453320) { Console.WriteLine("Invalid magic: " + magic); return(false); } } this.Endian = endian; uint lengthOfFilenameSection = stream.ReadUInt32(endian); // probably? uint startOfFilenameSection = stream.ReadUInt32(endian); // probably? DataBegin = stream.ReadUInt32(endian); stream.Position = startOfFilenameSection; uint magicOfFilenameSection = stream.ReadUInt32(endian); FileCount = stream.ReadUInt32(endian); Filenames = new List <string>((int)FileCount); for (uint i = 0; i < FileCount; ++i) { Filenames.Add(stream.ReadSizedString(48, encoding).TrimNull()); } Data = stream; return(true); }
public void PrintData(EndianUtils.Endianness endian, Dictionary <uint, TSS.TSSEntry> inGameDic, List <ItemDat.ItemDatSingle> itemDataSorted, T8BTEMST.T8BTEMST enemies) { using (DuplicatableStream stream = Stream.Duplicate()) { stream.ReadUInt32().FromEndian(endian); // ? stream.ReadUInt32().FromEndian(endian); // ? stream.ReadUInt32Array(9, endian); stream.ReadUInt32().FromEndian(endian); // play time in frames, assuming 60 frames = 1 second stream.ReadUInt32().FromEndian(endian); // gald stream.DiscardBytes(4); // ? uint[] itemCounts = stream.ReadUInt32Array(3072, endian); uint[] itemBookBitfields = stream.ReadUInt32Array(3072 / 32, endian); stream.DiscardBytes(4); // ? stream.ReadUInt32Array(4, endian); // control modes for the four active party slots stream.ReadUInt32Array(3, endian); // strategies assigned to dpad directions stream.DiscardBytes(0x40); // ?? for (int i = 0; i < 8; ++i) { // custom strategy names // game seems to read these till null byte so this could totally be abused to buffer overflow... stream.ReadAscii(0x40); } stream.DiscardBytes(0xA84D0 - 0xA7360); // ? uint[] monsterBookBitfieldsScanned = stream.ReadUInt32Array(0x48 / 4, endian); stream.DiscardBytes(0xA8680 - 0xA8518); // ? uint[] monsterBookBitfieldsSeen = stream.ReadUInt32Array(0x48 / 4, endian); stream.DiscardBytes(0xA8928 - 0xA86C8); // ? uint collectorsBookIndex = 0; foreach (var item in itemDataSorted) { uint i = item.Data[(int)ItemDat.ItemData.ID]; if (item.Data[(int)ItemDat.ItemData.InCollectorsBook] > 0) { bool haveItem = ((itemBookBitfields[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveItem ? "Y" : "N") + (collectorsBookIndex) + ": " + inGameDic[item.NamePointer].StringEngOrJpn); ++collectorsBookIndex; } } uint monsterBookIndex = 0; foreach (var enemy in enemies.EnemyList) { uint i = enemy.InGameID; if (enemy.InMonsterBook > 0) { bool haveSeen = ((monsterBookBitfieldsSeen[i / 32] >> (int)(i % 32)) & 1) > 0; bool haveScanned = ((monsterBookBitfieldsScanned[i / 32] >> (int)(i % 32)) & 1) > 0; Console.WriteLine((haveSeen ? "Y" : "N") + (haveScanned ? "Y" : "N") + (monsterBookIndex) + ": " + inGameDic[enemy.NameStringDicID].StringEngOrJpn); ++monsterBookIndex; } } } }
public void Add(SHA1 hash, DuplicatableStream stream, bool writeToBackup) { FileData ofd; if (Map.TryGetValue(hash, out ofd)) { using (var newdata = stream.Duplicate()) using (var olddata = ofd.Data.Duplicate()) { if (!StreamUtils.IsIdentical(newdata, olddata)) { throw new Exception("Storage already contains a different file for the given hash."); } } if (writeToBackup) { ofd.WriteToBackup = true; } return; } FileData fd = new FileData(stream.Duplicate(), writeToBackup); Map.Add(hash, fd); }
public SHBP(DuplicatableStream duplicatableStream, EndianUtils.Endianness e = EndianUtils.Endianness.BigEndian) { using (DuplicatableStream s = duplicatableStream.Duplicate()) { uint magic = s.ReadUInt32(EndianUtils.Endianness.LittleEndian); if (magic != 0x50424853) { throw new Exception("wrong magic"); } uint count = s.ReadUInt32(e); Hashes = new List <uint>((int)count); for (uint i = 0; i < count; ++i) { Hashes.Add(s.ReadUInt32(e)); } } }
public static DuplicatableStream Pack(List <SpkdPackFileData> packs, EndianUtils.Endianness e = EndianUtils.Endianness.BigEndian) { using (MemoryStream ms = new MemoryStream()) { ms.WriteUInt32(0x444B5053, EndianUtils.Endianness.LittleEndian); ms.WriteUInt32((uint)packs.Count, e); ms.WriteUInt32(0, e); ms.WriteUInt32(0x20, e); ms.WriteAlign(0x20); long headerstart = ms.Position; for (int i = 0; i < packs.Count; ++i) { var p = packs[i]; ms.WriteAscii(p.Name, 0x10); ms.WriteUInt32(p.Unknown, e); // file offsets, we'll fill these in later... ms.WriteUInt32(0xffffffffu, e); ms.WriteUInt32(0xffffffffu, e); ms.WriteUInt32(0xffffffffu, e); } for (int i = 0; i < packs.Count; ++i) { var p = packs[i]; for (int j = 0; j < 3; ++j) { DuplicatableStream inject = j == 0 ? p.File0 : j == 1 ? p.File1 : p.File2; if (inject != null) { long pos = ms.Position; ms.Position = headerstart + (i * 0x20) + 0x14 + (j * 4); ms.WriteUInt32((uint)pos, e); ms.Position = pos; using (var ds = inject.Duplicate()) { ds.Position = 0; StreamUtils.CopyStream(ds, ms); } ms.WriteAlign(0x10); } } } return(ms.CopyToByteArrayStreamAndDispose()); } }
public Dol(DuplicatableStream stream) { Stream = stream.Duplicate(); Stream.Position = 0; FileOffsets = new List <uint>(18); LoadingAddress = new List <uint>(18); SectionSizes = new List <uint>(18); for (int i = 0; i < 18; ++i) { FileOffsets.Add(Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian)); } for (int i = 0; i < 18; ++i) { LoadingAddress.Add(Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian)); } for (int i = 0; i < 18; ++i) { SectionSizes.Add(Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian)); } BssAddress = Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian); BssSize = Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian); EntryPoint = Stream.ReadUInt32().FromEndian(EndianUtils.Endianness.BigEndian); }
public SPKD(DuplicatableStream duplicatableStream, EndianUtils.Endianness e = EndianUtils.Endianness.BigEndian) { Stream = duplicatableStream.Duplicate(); Stream.Position = 4; uint fileCount = Stream.ReadUInt32(e); Stream.Position = 12; uint dataStart = Stream.ReadUInt32(e); Files = new List <SpkdFileData>((int)fileCount); Stream.Position = dataStart; for (uint i = 0; i < fileCount; ++i) { var f = new SpkdFileData(); f.Name = Stream.ReadAscii(16).TrimNull(); f.Unknown = Stream.ReadUInt32(e); f.FileStart0 = Stream.ReadUInt32(e); f.FileStart1 = Stream.ReadUInt32(e); f.FileStart2 = Stream.ReadUInt32(e); Files.Add(f); } LastFileEnd = (uint)Stream.Length; }
public static Stream ProcessAreaNameTexture(FileFetcher _fc, string name, DuplicatableStream jstream, DuplicatableStream ustream) { DuplicatableStream wstream = _fc.GetFile(name, Version.W); FPS4 wani = new FPS4(wstream.Duplicate()); FPS4 uani = new FPS4(new HyoutaTools.Tales.tlzc.TlzcDecompressor().Decompress(ustream.Duplicate())); var clgfileinfo = wani.Files.Where(x => x.FileName.EndsWith(".CLG")).First(); string clgname = clgfileinfo.FileName; FPS4 waniclg = new FPS4(wani.GetChildByName(clgname).AsFile.DataStream); FPS4 uaniclg = new FPS4(uani.GetChildByName(clgname).AsFile.DataStream); DuplicatableStream wtexstream = waniclg.GetChildByIndex(1).AsFile.DataStream; DuplicatableStream utexstream = uaniclg.GetChildByIndex(1).AsFile.DataStream; Stream wtexstreammod = ProcessTexture(name + "/" + clgname, utexstream, wtexstream); long injectOffset = clgfileinfo.Location.Value + waniclg.Files[1].Location.Value; Stream wstreammod = wstream.CopyToMemory(); wstreammod.Position = injectOffset; wtexstreammod.Position = 0; StreamUtils.CopyStream(wtexstreammod, wstreammod, wtexstreammod.Length); wstreammod.Position = 0; return(wstreammod); }
public iPck(DuplicatableStream duplicatableStream, EndianUtils.Endianness e = EndianUtils.Endianness.BigEndian) { using (DuplicatableStream s = duplicatableStream.Duplicate()) { uint magic = s.ReadUInt32(EndianUtils.Endianness.LittleEndian); if (magic != 0x6b635069) { throw new Exception("wrong magic"); } uint count = s.ReadUInt32(e); uint offsetsStart = s.ReadUInt32(e); uint dataStart = s.ReadUInt32(e); s.Position = offsetsStart; uint[] offsets = s.ReadUInt32Array(count, e); s.Position = dataStart; long dataEnd = s.Length - dataStart; Data = new List <byte[]>((int)count); for (uint i = 0; i < count; ++i) { long start = offsets[i]; if (start == 0xffffffffu) { Data.Add(null); } else { long end = FindFileEnd(offsets, i, dataEnd); long len = end - start; s.Position = dataStart + start; Data.Add(s.ReadUInt8Array(len)); } } } }
public static Stream ProcessTexture(string name, DuplicatableStream ustream, DuplicatableStream wstream) { FPS4 w = new FPS4(wstream.Duplicate()); TXM wtxm = new TXM(w.GetChildByIndex(0).AsFile.DataStream.Duplicate()); TXV wtxv = new TXV(wtxm, w.GetChildByIndex(1).AsFile.DataStream.Duplicate(), false); FPS4 u = new FPS4(ustream.Duplicate()); TXM utxm = new TXM(u.GetChildByIndex(0).AsFile.DataStream.Duplicate()); TXV utxv = new TXV(utxm, u.GetChildByIndex(1).AsFile.DataStream.Duplicate(), false); List <TexConvRules> convs = new List <TexConvRules>(); if (name == "rootR.cpk/mg/tex/karuta.tex") { convs.Add(new TexConvRules() { WTexId = 2, UTexId = 1, Method = TexConvMethod.Delegate, Delegate = (wtex, utex) => DoKarutaHalfAndHalf(wtex, utex, 82, 134, 294) }); convs.Add(new TexConvRules() { WTexId = 4, UTexId = 3, Method = TexConvMethod.Delegate, Delegate = (wtex, utex) => DoKarutaHalfAndHalf(wtex, utex, 86, 134, 294) }); convs.Add(new TexConvRules() { WTexId = 7, UTexId = 7, Method = TexConvMethod.DownscaleTwoThirds }); convs.Add(new TexConvRules() { WTexId = 13, UTexId = 13, Method = TexConvMethod.Delegate, Delegate = (wtex, utex) => DoKaruta13(wtex, utex) }); convs.Add(new TexConvRules() { WTexId = 17, UTexId = 17, Method = TexConvMethod.DownscaleTwoThirds }); convs.Add(new TexConvRules() { WTexId = 22, UTexId = 23, Method = TexConvMethod.Delegate, Delegate = (wtex, utex) => DownscaleTwoThirds(CropExpandCanvas(utex, 2, -2, (uint)(utex.Width + 3), (uint)(utex.Height - 3))) }); } else if (name == "rootR.cpk/mnu/tex/main.tex") { convs.Add(new TexConvRules() { WTexId = 110, UTexId = 61, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 111, UTexId = 62, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 112, UTexId = 63, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 113, UTexId = 64, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 114, UTexId = 65, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 115, UTexId = 66, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 116, UTexId = 67, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 117, UTexId = 68, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 118, UTexId = 69, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 119, UTexId = 70, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 124, UTexId = 75, Method = TexConvMethod.Downscale2x }); } else if (name == "rootR.cpk/mnu/tex/shop.tex") { convs.Add(new TexConvRules() { WTexId = 1, UTexId = 1, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 2, UTexId = 2, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 3, UTexId = 3, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 4, UTexId = 4, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 5, UTexId = 5, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 6, UTexId = 6, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 7, UTexId = 7, Method = TexConvMethod.Downscale2x }); } else if (name == "rootR.cpk/mnu/tex/skill.tex") { convs.Add(new TexConvRules() { WTexId = 0, UTexId = 0, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 1, UTexId = 1, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 2, UTexId = 2, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 3, UTexId = 3, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 4, UTexId = 4, Method = TexConvMethod.Downscale2x }); convs.Add(new TexConvRules() { WTexId = 5, UTexId = 5, Method = TexConvMethod.Downscale2x }); } else if (name == "rootR.cpk/mnu/tex/snd_test.tex") { convs.Add(new TexConvRules() { WTexId = 1, UTexId = 1, Method = TexConvMethod.Downscale2x }); } else if (name == "rootR.cpk/SysSub/JA/TitleTexture.tex") { convs.Add(new TexConvRules() { WTexId = 1, UTexId = 1, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 2, UTexId = 4, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 3, UTexId = 6, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 4, UTexId = 8, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 6, UTexId = 12, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 7, UTexId = 14, Method = TexConvMethod.CropExpandCanvas }); convs.Add(new TexConvRules() { WTexId = 8, UTexId = 16, Method = TexConvMethod.CropExpandCanvas }); } else if (name.EndsWith(".CLG")) { convs.Add(new TexConvRules() { WTexId = 0, UTexId = 0, Method = TexConvMethod.DownscaleTwoThirds }); convs.Add(new TexConvRules() { WTexId = 1, UTexId = 1, Method = TexConvMethod.Clear }); } MemoryStream s = wstream.Duplicate().CopyToMemory(); s.Position = 0; foreach (TexConvRules c in convs) { var wm = wtxm.TXMRegulars[c.WTexId]; var um = utxm.TXMRegulars[c.UTexId]; var wv = wtxv.textures.Where(x => x.TXM == wm).First(); var uv = utxv.textures.Where(x => x.TXM == um).First(); System.Drawing.Bitmap newImage = null; switch (c.Method) { case TexConvMethod.Downscale2x: { newImage = FontProcessing.DownscaleInteger(uv.GetBitmaps()[0], 2); } break; case TexConvMethod.DownscaleTwoThirds: { newImage = DownscaleTwoThirds(uv.GetBitmaps()[0]); } break; case TexConvMethod.CropExpandCanvas: { newImage = DoCropExpandCanvas(uv.GetBitmaps()[0], wm.Width, wm.Height); } break; case TexConvMethod.Clear: { newImage = new Bitmap(wv.GetBitmaps()[0]); for (int y = 0; y < newImage.Height; ++y) { for (int x = 0; x < newImage.Width; ++x) { newImage.SetPixel(x, y, Color.FromArgb(0, 0, 0, 0)); } } } break; case TexConvMethod.Delegate: { newImage = c.Delegate(wv.GetBitmaps()[0], uv.GetBitmaps()[0]); } break; default: { throw new Exception("don't know how to convert " + uv.TXM.Name); } } if (newImage != null) { HyoutaTools.Util.Assert(newImage.Width == wm.Width); HyoutaTools.Util.Assert(newImage.Height == wm.Height); if (wm.Format == HyoutaTools.Tales.Vesperia.Texture.TextureFormat.Indexed8Bits_RGB5A3) { ChopBitsRGB5A3(newImage); var palette = GeneratePalette256(newImage); s.Position = w.Files[1].Location.Value + wm.TxvLocation; foreach (var loc in new HyoutaTools.Textures.PixelOrderIterators.TiledPixelOrderIterator(newImage.Width, newImage.Height, 8, 4)) { int cval = 0; if (loc.X < newImage.Width && loc.Y < newImage.Height) { cval = palette.lookup[newImage.GetPixel(loc.X, loc.Y)]; } s.WriteByte((byte)cval); } for (int ci = 0; ci < 256; ++ci) { ushort cval = 0; if (ci < palette.colors.Count) { cval = HyoutaTools.Textures.ColorFetchingIterators.ColorFetcherRGB5A3.ColorToRGB5A3(palette.colors[ci]); } s.WriteUInt16(cval.ToEndian(EndianUtils.Endianness.BigEndian)); } } else if (wm.Format == HyoutaTools.Tales.Vesperia.Texture.TextureFormat.GamecubeRGBA8) { s.Position = w.Files[1].Location.Value + wm.TxvLocation; byte[] tmpb = new byte[0x40]; int tmpp = 0; foreach (var loc in new HyoutaTools.Textures.PixelOrderIterators.TiledPixelOrderIterator(newImage.Width, newImage.Height, 4, 4)) { Color col = newImage.GetPixel(loc.X, loc.Y); tmpb[tmpp * 2 + 0] = col.A; tmpb[tmpp * 2 + 1] = col.R; tmpb[tmpp * 2 + 0 + 0x20] = col.G; tmpb[tmpp * 2 + 1 + 0x20] = col.B; ++tmpp; if (tmpp == 16) { tmpp = 0; s.Write(tmpb); } } if (tmpp != 0) { throw new Exception("Unexpected tile size for " + wm.Name); } } else { Console.WriteLine("don't know how to encode into " + wm.Format); } } } s.Position = 0; return(s); }
public SaveDataBlockSavePoint(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public void PrintData() { // save point flags, one byte each, 0x00 not visted 0x01 visited byte[] savePointFlags; using (DuplicatableStream stream = Stream.Duplicate()) { savePointFlags = stream.ReadUInt8Array(0x59); } PrintSavePoint(savePointFlags, 0x00, "Fiertia Deck (Docked at Atherum)"); PrintSavePoint(savePointFlags, 0x01, "Atherum"); PrintSavePoint(savePointFlags, 0x02, "Fiertia Hold"); // same flag for both opportunities? PrintSavePoint(savePointFlags, 0x03, "Keiv Moc (Middle)"); PrintSavePoint(savePointFlags, 0x04, "Keiv Moc (Boss)"); PrintSavePoint(savePointFlags, 0x05, "Zaphias (Lower Quarter)"); PrintSavePoint(savePointFlags, 0x06, "Zaphias (Royal Quarter)"); PrintSavePoint(savePointFlags, 0x07, "Zaphias Castle (Prison)"); PrintSavePoint(savePointFlags, 0x08, "Zaphias Castle (Kitchen)"); // 2nd visit only PrintSavePoint(savePointFlags, 0x09, "Zaphias Castle (Hallways)"); // before zagi fight PrintSavePoint(savePointFlags, 0x0A, "Zaphias Castle (Sword Stair)"); PrintSavePoint(savePointFlags, 0x0B, "Zaphias Castle (Big Hall)"); // 2nd visit only, that big room that leads to the sword stair PrintSavePoint(savePointFlags, 0x0C, "Weasand of Cados (Middle)"); PrintSavePoint(savePointFlags, 0x0D, "Weasand of Cados (Exit)"); PrintSavePoint(savePointFlags, 0x0E, "Halure (Inn)"); PrintSavePoint(savePointFlags, 0x0F, "Ghasfarost (Bottom)"); PrintSavePoint(savePointFlags, 0x10, "Ghasfarost (Top)"); PrintSavePoint(savePointFlags, 0x11, "Myorzo (Vacant House)"); PrintSavePoint(savePointFlags, 0x12, "Mt. Temza (Middle)"); PrintSavePoint(savePointFlags, 0x13, "Mt. Temza (Boss)"); PrintSavePoint(savePointFlags, 0x14, "Deidon Hold"); PrintSavePoint(savePointFlags, 0x15, "Northeastern Hypionia"); // aurnion before it's built PrintSavePoint(savePointFlags, 0x16, "Aurnion (Developing)"); PrintSavePoint(savePointFlags, 0x17, "Aurnion (Developed)"); PrintSavePoint(savePointFlags, 0x18, "Caer Bocram"); PrintSavePoint(savePointFlags, 0x19, "Quoi Woods"); PrintSavePoint(savePointFlags, 0x1A, "Dahngrest (Inn)"); PrintSavePoint(savePointFlags, 0x1B, "Ehmead Hill"); PrintSavePoint(savePointFlags, 0x1C, "Erealumen (Middle)"); PrintSavePoint(savePointFlags, 0x1D, "Erealumen (Boss)"); PrintSavePoint(savePointFlags, 0x1E, "Heracles (Near Engine Room)"); PrintSavePoint(savePointFlags, 0x1F, "Heracles (Near Control Room)"); // zagi fight PrintSavePoint(savePointFlags, 0x20, "Zopheir (Boss)"); // 1st visit only PrintSavePoint(savePointFlags, 0x21, "Zopheir (Near Aer Krene)"); // 2nd visit only PrintSavePoint(savePointFlags, 0x22, "Manor of the Wicked"); PrintSavePoint(savePointFlags, 0x23, "Tarqaron (Middle)"); PrintSavePoint(savePointFlags, 0x24, "Tarqaron (Top)"); PrintSavePoint(savePointFlags, 0x25, "Baction B1F"); PrintSavePoint(savePointFlags, 0x26, "Baction B2F"); // both save points on B2F share this flag...? PrintSavePoint(savePointFlags, 0x27, "Mantaic (Inn)"); PrintSavePoint(savePointFlags, 0x28, "Relewiese (Middle)"); PrintSavePoint(savePointFlags, 0x29, "Relewiese (Boss)"); PrintSavePoint(savePointFlags, 0x2A, "Capua Nor (Outside Ragou's Mansion)"); PrintSavePoint(savePointFlags, 0x2B, "Capua Nor (Inn)"); PrintSavePoint(savePointFlags, 0x2C, "Capua Torim (Inn)"); PrintSavePoint(savePointFlags, 0x2D, "Shaikos Ruins"); PrintSavePoint(savePointFlags, 0x2E, "Zaude (Side Entrance)"); PrintSavePoint(savePointFlags, 0x2F, "Zaude (Alexei)"); PrintSavePoint(savePointFlags, 0x30, "Zaude (Yeager)"); PrintSavePoint(savePointFlags, 0x31, "Aspio (Inn)"); PrintSavePoint(savePointFlags, 0x32, "Nordopolica (Inn)"); PrintSavePoint(savePointFlags, 0x33, "Heliord (Inn)"); PrintSavePoint(savePointFlags, 0x34, "Yormgen (Inn)"); PrintSavePoint(savePointFlags, 0x35, "Weasand of Kogorh (Oasis)"); PrintSavePoint(savePointFlags, 0x36, "Weasand of Kogorh (Exit)"); PrintSavePoint(savePointFlags, 0x37, "Egothor Forest"); PrintSavePoint(savePointFlags, 0x38, "Dahngrest Underpass (Oath)"); PrintSavePoint(savePointFlags, 0x39, "Ragou's Mansion"); // basement dungeon midpoint PrintSavePoint(savePointFlags, 0x3A, "Dahngrest Underpass (Exit)"); PrintSavePoint(savePointFlags, 0x3B, "Abysmal Hollow (Aer Krene near Yumanju)"); PrintSavePoint(savePointFlags, 0x3C, "? Abysmal Hollow (Aer Krene near Zaphias)"); PrintSavePoint(savePointFlags, 0x3D, "Abysmal Hollow (Aer Krene near Heliord)"); PrintSavePoint(savePointFlags, 0x3E, "Abysmal Hollow (Aer Krene near Nordopolica)"); PrintSavePoint(savePointFlags, 0x3F, "? Abysmal Hollow (Center)"); PrintSavePoint(savePointFlags, 0x40, "City of the Waning Moon"); PrintSavePoint(savePointFlags, 0x41, "Necropolis of Nostalgia A3"); PrintSavePoint(savePointFlags, 0x42, "Necropolis of Nostalgia A6"); PrintSavePoint(savePointFlags, 0x43, "Necropolis of Nostalgia A9"); PrintSavePoint(savePointFlags, 0x44, "Necropolis of Nostalgia A Bottom"); PrintSavePoint(savePointFlags, 0x45, "Necropolis of Nostalgia B2"); PrintSavePoint(savePointFlags, 0x46, "Necropolis of Nostalgia B5"); PrintSavePoint(savePointFlags, 0x47, "Necropolis of Nostalgia B8"); PrintSavePoint(savePointFlags, 0x48, "Necropolis of Nostalgia B Bottom"); PrintSavePoint(savePointFlags, 0x49, "Necropolis of Nostalgia C3"); PrintSavePoint(savePointFlags, 0x4A, "Necropolis of Nostalgia C6"); PrintSavePoint(savePointFlags, 0x4B, "Necropolis of Nostalgia C9"); PrintSavePoint(savePointFlags, 0x4C, "Necropolis of Nostalgia C Bottom"); PrintSavePoint(savePointFlags, 0x4D, "Necropolis of Nostalgia D3"); PrintSavePoint(savePointFlags, 0x4E, "Necropolis of Nostalgia D6"); PrintSavePoint(savePointFlags, 0x4F, "Necropolis of Nostalgia D9"); PrintSavePoint(savePointFlags, 0x50, "Necropolis of Nostalgia D Bottom"); PrintSavePoint(savePointFlags, 0x51, "Necropolis of Nostalgia E3"); PrintSavePoint(savePointFlags, 0x52, "Necropolis of Nostalgia E6"); PrintSavePoint(savePointFlags, 0x53, "Necropolis of Nostalgia E9"); PrintSavePoint(savePointFlags, 0x54, "Necropolis of Nostalgia E Bottom"); PrintSavePoint(savePointFlags, 0x55, "Necropolis of Nostalgia F3"); PrintSavePoint(savePointFlags, 0x56, "Necropolis of Nostalgia F6"); PrintSavePoint(savePointFlags, 0x57, "Necropolis of Nostalgia F9"); PrintSavePoint(savePointFlags, 0x58, "Necropolis of Nostalgia F Bottom"); }
public static (DuplicatableStream metrics, DuplicatableStream texture, Dictionary <char, (int w1, int w2)> charToWidthMap) Run(FileFetcher _fc, Config config) { bool debug = config.DebugFontOutputPath != null; bool adjustMetrics = debug; DuplicatableStream metricsWiiStream = _fc.GetFile("rootR.cpk/sys/FontBinary2.bin", Version.W); DuplicatableStream textureWiiStream = _fc.GetFile("rootR.cpk/sys/FontTexture2.tex", Version.W); DuplicatableStream texturePs3Stream = _fc.GetFile("rootR.cpk/sys/FontTexture2.tex", Version.U); FPS4 metricsWiiFps4 = new FPS4(metricsWiiStream); DuplicatableStream metricsWiiData = metricsWiiFps4.GetChildByIndex(1).AsFile.DataStream; FPS4 textureWiiFps4 = new FPS4(textureWiiStream); FPS4 texturePs3Fps4 = new FPS4(texturePs3Stream); TXM textureWiiTxm = new TXM(textureWiiFps4.GetChildByIndex(0).AsFile.DataStream); TXV textureWiiTxv = new TXV(textureWiiTxm, textureWiiFps4.GetChildByIndex(1).AsFile.DataStream, false); TXM texturePs3Txm = new TXM(texturePs3Fps4.GetChildByIndex(0).AsFile.DataStream); TXV texturePs3Txv = new TXV(texturePs3Txm, texturePs3Fps4.GetChildByIndex(1).AsFile.DataStream, false); Bitmap bitmapWii = textureWiiTxv.textures[0].GetBitmaps()[0]; Bitmap bitmapPs3 = texturePs3Txv.textures[0].GetBitmaps()[0]; if (debug) { Directory.CreateDirectory(config.DebugFontOutputPath); bitmapWii.Save(Path.Combine(config.DebugFontOutputPath, "wii.png")); bitmapPs3.Save(Path.Combine(config.DebugFontOutputPath, "ps3.png")); } var img_wii = bitmapWii; var img_ps3 = bitmapPs3; const int tile_extent_in_image = 25; const int tile_extent_actual = 24; int tiles_x = (img_wii.Width + 1) / tile_extent_in_image; int tiles_y = (img_wii.Height + 1) / tile_extent_in_image; const int ps3_tile_extent_in_image = 37; const int ps3_tile_extent_actual = 36; int ps3_tiles_x = (img_ps3.Width + 1) / ps3_tile_extent_in_image; int ps3_tiles_y = (img_ps3.Height + 1) / ps3_tile_extent_in_image; // split into individual tiles and extract source colors HashSet <Color> colors = new HashSet <Color>(); List <Bitmap> tiles_wii = new List <Bitmap>(); List <Bitmap> tiles_ps3 = new List <Bitmap>(); for (int ty = 0; ty < tiles_y; ++ty) { for (int tx = 0; tx < tiles_x; ++tx) { var bmp = new Bitmap(tile_extent_actual, tile_extent_actual); for (int y = 0; y < tile_extent_actual; ++y) { for (int x = 0; x < tile_extent_actual; ++x) { var px = img_wii.GetPixel(tx * tile_extent_in_image + x, ty * tile_extent_in_image + y); colors.Add(px); bmp.SetPixel(x, y, px); } } tiles_wii.Add(bmp); } } for (int ty = 0; ty < ps3_tiles_y; ++ty) { for (int tx = 0; tx < ps3_tiles_x; ++tx) { var bmp = new Bitmap(ps3_tile_extent_actual, ps3_tile_extent_actual); for (int y = 0; y < ps3_tile_extent_actual; ++y) { for (int x = 0; x < ps3_tile_extent_actual; ++x) { var px = img_ps3.GetPixel(tx * ps3_tile_extent_in_image + x, ty * ps3_tile_extent_in_image + y); bmp.SetPixel(x, y, px); } } tiles_ps3.Add(bmp); } } // inject ps3 tiles over wii tiles List <(int where, int ps3where, string chars)> charsets = new List <(int where, int ps3where, string chars)>(); charsets.Add((0, 0, "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz")); charsets.Add((243, 74, ",.")); charsets.Add((246, 77, ":;?!_")); charsets.Add((254, 83, "/\\~|…")); charsets.Add((260, 118, "‘")); charsets.Add((261, 118, "’")); charsets.Add((262, 119, "“")); charsets.Add((263, 119, "”")); charsets.Add((264, 93, "()[]{}")); charsets.Add((276, 105, "+")); charsets.Add((277, 82, "-")); // copy the dash minus instead of the math minus, looks better in text flow charsets.Add((278, 107, "±×÷=≠<>≤≥")); charsets.Add((289, 118, "'\"")); charsets.Add((293, 122, "$%#&*@")); Dictionary <char, (int w1, int w2)> charToWidthMap = new Dictionary <char, (int w1, int w2)>(); byte[] metrics = new byte[metricsWiiData.Length]; metricsWiiData.Read(metrics, 0, metrics.Length); foreach (var charset in charsets) { int where = charset.where; int ps3where = charset.ps3where; foreach (char ch in charset.chars) { var wiitile = tiles_wii[where]; var averagescaled = DownscaleTileFromPs3ToWiiWithUnweightedAverageScaling(tiles_ps3[ps3where]); //var downscaled = DownscaleTileFromPs3ToWiiWithWeightedScaling(tiles_ps3[ps3where]); var downscaled = averagescaled; PosterizeImage(wiitile, downscaled, colors, tile_extent_actual); PosterizeImage(averagescaled, averagescaled, colors, tile_extent_actual); if (debug) { wiitile.Save(Path.Combine(config.DebugFontOutputPath, string.Format("wii_new_{0:D4}.png", where))); } int cutoff_1 = 180; int cutoff_2 = 220; int leftwhere = where * 8 + 0; int rightwhere = where * 8 + 1; int leftwhere2 = where * 8 + 4; int rightwhere2 = where * 8 + 5; // forcing vertical extents to be the same for all, because text looks *really weird* in english if lines have different heights // for digits, forcing horizontal extents to be the same as well so they look nice in vertical lists bool isDigit = ch == '0' || ch == '1' || ch == '2' || ch == '3' || ch == '4' || ch == '5' || ch == '6' || ch == '7' || ch == '8' || ch == '9'; if (isDigit) { metrics[leftwhere] = ch == '1' ? (byte)(6) : ch == '2' ? (byte)(6) : (byte)(7); metrics[rightwhere] = ch == '1' ? (byte)(8) : ch == '2' ? (byte)(8) : (byte)(7); //metrics[leftwhere2] = ch == '1' ? (byte)(7) : ch == '2' ? (byte)(7) : (byte)(8); //metrics[rightwhere2] = ch == '1' ? (byte)(9) : ch == '2' ? (byte)(9) : (byte)(8); } else { metrics[leftwhere] = (byte)MeasureAlphaFromLeft(averagescaled, cutoff_1); metrics[rightwhere] = (byte)MeasureAlphaFromRight(averagescaled, cutoff_1); //metrics[leftwhere2] = (byte)MeasureAlphaFromLeft(averagescaled, cutoff_2); //metrics[rightwhere2] = (byte)MeasureAlphaFromRight(averagescaled, cutoff_2); } switch (ch) { case 'j': case ';': metrics[leftwhere] += 1; break; case 'A': case 'P': case 'Q': case 'T': case 'Y': case 'W': case 'f': case 's': case 'w': case 'y': case '[': metrics[rightwhere] += 1; break; case 't': metrics[leftwhere] += 1; metrics[rightwhere] += 1; break; default: break; } metrics[leftwhere2] = metrics[leftwhere]; metrics[rightwhere2] = metrics[rightwhere]; switch (ch) { case '.': case ',': metrics[leftwhere2] += 1; metrics[rightwhere] += 1; break; default: break; } metrics[where * 8 + 2] = 0; //(byte)MeasureAlphaFromTop(test, cutoff_1); metrics[where * 8 + 3] = 4; //(byte)MeasureAlphaFromBottom(test, cutoff_1); metrics[where * 8 + 6] = 0; //(byte)MeasureAlphaFromTop(test, cutoff_2); metrics[where * 8 + 7] = 4; //(byte)MeasureAlphaFromBottom(test, cutoff_2); int width1 = tile_extent_actual - (metrics[leftwhere] + metrics[rightwhere]); int width2 = tile_extent_actual - (metrics[leftwhere2] + metrics[rightwhere2]); charToWidthMap.Add(ch, (width1, width2)); ++where; ++ps3where; } } // manually generate good metrics for space; see also code patches in main.dol metrics[0x6F70] = 10; metrics[0x6F71] = 10; metrics[0x6F72] = 0; metrics[0x6F73] = 4; metrics[0x6F74] = 10; metrics[0x6F75] = 10; metrics[0x6F76] = 0; metrics[0x6F77] = 4; charToWidthMap.Add(' ', (tile_extent_actual - 20, tile_extent_actual - 20)); // write out visual representation of font metrics for adjustments if (adjustMetrics) { foreach (var charset in charsets) { int where = charset.where; foreach (char ch in charset.chars) { int factor = 10; var test = PointScale(tiles_wii[where], factor); for (int metricsset = 0; metricsset < 2; ++metricsset) { Color col = metricsset == 0 ? Color.Red : Color.Yellow; int xl = (metrics[where * 8 + metricsset * 4] - 1) * factor + factor - 1; int xr = (tiles_wii[where].Width - metrics[where * 8 + metricsset * 4 + 1]) * factor; int yt = (metrics[where * 8 + metricsset * 4 + 2] - 1) * factor + factor - 1; int yb = (tiles_wii[where].Width - metrics[where * 8 + metricsset * 4 + 3]) * factor; for (int y = 0; y < test.Height; ++y) { if (xl >= 0 && xl < test.Width) { test.SetPixel(xl, y, col); } if (xr >= 0 && xr < test.Width) { test.SetPixel(xr, y, col); } } for (int x = 0; x < test.Width; ++x) { if (yt >= 0 && yt < test.Height) { test.SetPixel(x, yt, col); } if (yb >= 0 && yb < test.Height) { test.SetPixel(x, yb, col); } } } PointScale(test, 3).Save(Path.Combine(config.DebugFontOutputPath, string.Format("metrics_view_{0:D4}.png", where))); ++where; } } } // join indvidiual tiles back into full texture int idx = 0; for (int ty = 0; ty < tiles_y; ++ty) { for (int tx = 0; tx < tiles_x; ++tx) { var bmp = tiles_wii[idx]; for (int y = 0; y < tile_extent_actual; ++y) { for (int x = 0; x < tile_extent_actual; ++x) { var px = bmp.GetPixel(x, y); img_wii.SetPixel(tx * tile_extent_in_image + x, ty * tile_extent_in_image + y, px); } } ++idx; } } if (debug) { img_wii.Save(Path.Combine(config.DebugFontOutputPath, "wii_new.png")); } // inject metrics DuplicatableStream outputMetricsStream; { Stream stream = metricsWiiStream.Duplicate().CopyToMemory(); stream.Position = 0x43E0; stream.Write(metrics); stream.Position = 0; byte[] data = new byte[stream.Length]; stream.Read(data, 0, data.Length); outputMetricsStream = new HyoutaUtils.Streams.DuplicatableByteArrayStream(data); } // encode texture DuplicatableStream outputTextureStream; { Stream stream = textureWiiStream.Duplicate().CopyToMemory(); stream.Position = 0x80100; List <(int idx, ushort v)> stuff = new List <(int idx, ushort v)>(); for (int i = 0; i < 16; ++i) { stuff.Add((i, stream.ReadUInt16().FromEndian(EndianUtils.Endianness.BigEndian))); } stream.Position = 0x100; var pxit = new HyoutaTools.Textures.PixelOrderIterators.TiledPixelOrderIterator(img_wii.Width, img_wii.Height, 8, 8); byte storage = 0; bool even = false; foreach (var px in pxit) { if (px.X < img_wii.Width && px.Y < img_wii.Height) { Color col = img_wii.GetPixel(px.X, px.Y); ushort value = HyoutaTools.Textures.ColorFetchingIterators.ColorFetcherGrey8Alpha8.ColorToGrey8Alpha8(col); var colidx = stuff.First(x => x.v == value).idx; if (!even) { storage = (byte)colidx; } else { storage = (byte)(storage << 4 | (byte)colidx); stream.WriteByte(storage); } even = !even; } } stream.Position = 0; byte[] data = new byte[stream.Length]; stream.Read(data, 0, data.Length); outputTextureStream = new HyoutaUtils.Streams.DuplicatableByteArrayStream(data); } return(outputMetricsStream, outputTextureStream, charToWidthMap); }
private static Stream RebuildNubStream(DuplicatableStream wstream, string nubdir, string nubtype, GetFilenameDelegate getFilenameDelegate) { MemoryStream outstream = new MemoryStream(); EndianUtils.Endianness e = EndianUtils.Endianness.BigEndian; using (var stream = wstream.Duplicate()) { stream.Position = 0; var header = new HyoutaTools.Tales.Vesperia.NUB.NubHeader(stream, e); stream.Position = 0; StreamUtils.CopyStream(stream, outstream, header.StartOfFiles); stream.Position = header.StartOfEntries; uint[] entries = stream.ReadUInt32Array(header.EntryCount, e); for (long i = 0; i < entries.LongLength; ++i) { uint entryLoc = entries[i]; if (nubtype == "bnsf") { using (var bnsfstream = new DuplicatableFileStream(Path.Combine(nubdir, getFilenameDelegate(i) + ".rawbnsf"))) using (var samplecountstream = new DuplicatableFileStream(Path.Combine(nubdir, getFilenameDelegate(i) + ".samplecount"))) { // write file to outstream long filestart = outstream.Position; StreamUtils.CopyStream(bnsfstream, outstream, bnsfstream.Length); outstream.WriteAlign(0x10); long fileend = outstream.Position; long filelen = fileend - filestart; // update headers outstream.Position = entryLoc + 0xbc + 0x4; outstream.WriteUInt32((uint)(bnsfstream.Length + 0x28), e); outstream.Position = entryLoc + 0xbc + 0x1c; outstream.WriteUInt32((uint)(samplecountstream.ReadUInt64(EndianUtils.Endianness.LittleEndian)), e); outstream.Position = entryLoc + 0xbc + 0x2c; outstream.WriteUInt32((uint)(bnsfstream.Length), e); outstream.Position = entryLoc + 0x14; outstream.WriteUInt32((uint)filelen, e); outstream.WriteUInt32((uint)(filestart - header.StartOfFiles), e); outstream.Position = fileend; } } else if (nubtype == "dsp") { using (var fs = new DuplicatableFileStream(Path.Combine(nubdir, getFilenameDelegate(i) + ".dsp"))) { byte[] dspheader = fs.ReadUInt8Array(0x60); // write file to outstream long filestart = outstream.Position; StreamUtils.CopyStream(fs, outstream, fs.Length - 0x60); outstream.WriteAlign(0x10); long fileend = outstream.Position; long filelen = fileend - filestart; // update headers outstream.Position = entryLoc + 0xbc; outstream.Write(dspheader); outstream.Position = entryLoc + 0x14; outstream.WriteUInt32((uint)filelen, e); outstream.WriteUInt32((uint)(filestart - header.StartOfFiles), e); outstream.Position = fileend; } } } long filesSize = outstream.Position - header.StartOfFiles; outstream.Position = 0x14; outstream.WriteUInt32((uint)filesSize, e); outstream.Position = 0; return(outstream); } }
public HyoutaArchiveChunk(DuplicatableStream duplicatableStream, out ulong chunkLength) { using (DuplicatableStream data = duplicatableStream.Duplicate()) { data.Position = 0; // header ulong extraMagic = data.ReadUInt64(EndianUtils.Endianness.LittleEndian); ulong magic = extraMagic & 0x00fffffffffffffful; if (magic != 0x6b6e7568636168) { throw new Exception("wrong magic"); } byte extra = (byte)((extraMagic >> 56) & 0xffu); byte packedAlignment = (byte)(extra & 0x1fu); long unpackedAlignment = 1L << packedAlignment; bool hasMetadata = (extra & 0x20) != 0; bool isCompressed = (extra & 0x40) != 0; bool isBigEndian = (extra & 0x80) != 0; EndianUtils.Endianness e = isBigEndian ? EndianUtils.Endianness.BigEndian : EndianUtils.Endianness.LittleEndian; ulong endOfFileOffset = data.ReadUInt64(e) << packedAlignment; ulong tableOfContentsOffset = data.ReadUInt64(e) << packedAlignment; ulong filecount = data.ReadUInt64(e); chunkLength = endOfFileOffset; if (hasMetadata) { // just skip past this for now ulong metadataLength = data.ReadUInt64(e); data.DiscardBytes(metadataLength); } DuplicatableStream dataBlockStream; if (isCompressed) { ushort compressionInfoLengthRaw = data.ReadUInt16(e); uint compressionInfoLength = compressionInfoLengthRaw & 0xfffcu; int compressionInfoAlignmentPacked = (compressionInfoLengthRaw & 0x3) + 1; data.ReadAlign(1u << compressionInfoAlignmentPacked); Compression.IHyoutaArchiveCompressionInfo?compressionInfo = HyoutaArchiveCompression.Deserialize(data, compressionInfoLength == 0 ? 0x10000u : compressionInfoLength, e); if (compressionInfo == null) { throw new Exception("File is indicated to be compressed, but no decompressor found."); } dataBlockStream = compressionInfo.Decompress(data); } else { data.ReadAlign(unpackedAlignment); dataBlockStream = new PartialStream(data, data.Position, (long)(endOfFileOffset - (ulong)data.Position)); } try { data.Dispose(); dataBlockStream.Position = (long)tableOfContentsOffset; uint offsetToFirstFileInfo = ReadContentLength(dataBlockStream, e); // decode content bitfield(s) long numberOfUnknownBits = 0; ushort contentBitfield1 = dataBlockStream.ReadUInt16(e); bool hasDummyContent = (contentBitfield1 & 0x0001u) != 0; bool hasFilename = (contentBitfield1 & 0x0002u) != 0; bool hasCompression = (contentBitfield1 & 0x0004u) != 0; bool hasBpsPatch = (contentBitfield1 & 0x0008u) != 0; bool hasCrc32 = (contentBitfield1 & 0x0010u) != 0; bool hasMd5 = (contentBitfield1 & 0x0020u) != 0; bool hasSha1 = (contentBitfield1 & 0x0040u) != 0; numberOfUnknownBits += (contentBitfield1 & 0x0080u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x0100u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x0200u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x0400u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x0800u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x1000u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x2000u) != 0 ? 1 : 0; numberOfUnknownBits += (contentBitfield1 & 0x4000u) != 0 ? 1 : 0; ushort currentBitfield = contentBitfield1; while ((currentBitfield & 0x8000u) != 0) { // more bitfields, though we don't understand them since only the first handful of bits are defined at the moment, so just count and skip them currentBitfield = dataBlockStream.ReadUInt16(e); numberOfUnknownBits += (currentBitfield & 0x0001u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0002u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0004u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0008u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0010u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0020u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0040u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0080u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0100u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0200u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0400u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x0800u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x1000u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x2000u) != 0 ? 1 : 0; numberOfUnknownBits += (currentBitfield & 0x4000u) != 0 ? 1 : 0; } uint dummyContentLength = hasDummyContent ? ReadContentLength(dataBlockStream, e) : 0; uint filenameLength = hasFilename ? ReadContentLength(dataBlockStream, e) : 0; uint compressionLength = hasCompression ? ReadContentLength(dataBlockStream, e) : 0; uint bpspatchLength = hasBpsPatch ? ReadContentLength(dataBlockStream, e) : 0; uint crc32Length = hasCrc32 ? ReadContentLength(dataBlockStream, e) : 0; uint md5Length = hasMd5 ? ReadContentLength(dataBlockStream, e) : 0; uint sha1Length = hasSha1 ? ReadContentLength(dataBlockStream, e) : 0; long unknownContentLength = 0; for (long i = 0; i < numberOfUnknownBits; ++i) { unknownContentLength += ReadContentLength(dataBlockStream, e); } dataBlockStream.Position = (long)(tableOfContentsOffset + offsetToFirstFileInfo); List <HyoutaArchiveFileInfo> files = new List <HyoutaArchiveFileInfo>((int)filecount); for (ulong i = 0; i < filecount; ++i) { ulong offset = dataBlockStream.ReadUInt64(e) << packedAlignment; ulong filesize = dataBlockStream.ReadUInt64(e); HyoutaArchiveFileInfo fi = new HyoutaArchiveFileInfo(); if (hasDummyContent) { fi.DummyContent = dataBlockStream.ReadBytes(dummyContentLength); } if (hasFilename) { fi.Filename = ReadString(dataBlockStream, filenameLength, e); } if (hasCompression) { fi.CompressionInfo = HyoutaArchiveCompression.Deserialize(dataBlockStream, compressionLength, e); fi.StreamIsCompressed = true; } if (hasBpsPatch) { fi.BpsPatchInfo = HyoutaArchiveBpsPatchInfo.Deserialize(dataBlockStream, bpspatchLength, e, i, this); fi.StreamIsBpsPatch = fi.BpsPatchInfo != null; } if (hasCrc32) { if (crc32Length >= 4) { fi.crc32 = new CRC32(dataBlockStream.ReadUInt32(EndianUtils.Endianness.BigEndian)); dataBlockStream.DiscardBytes(crc32Length - 4); } else { dataBlockStream.DiscardBytes(crc32Length); } } if (hasMd5) { if (md5Length >= 16) { ulong a = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian); ulong b = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian); fi.md5 = new MD5(a, b); dataBlockStream.DiscardBytes(md5Length - 16); } else { dataBlockStream.DiscardBytes(md5Length); } } if (hasSha1) { if (sha1Length >= 20) { ulong a = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian); ulong b = dataBlockStream.ReadUInt64(EndianUtils.Endianness.BigEndian); uint c = dataBlockStream.ReadUInt32(EndianUtils.Endianness.BigEndian); fi.sha1 = new SHA1(a, b, c); dataBlockStream.DiscardBytes(sha1Length - 20); } else { dataBlockStream.DiscardBytes(sha1Length); } } dataBlockStream.DiscardBytes(unknownContentLength); fi.Data = new PartialStream(dataBlockStream, (long)offset, (long)filesize); files.Add(fi); } Files = files; } finally { dataBlockStream.Dispose(); } } }
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); }
public void PrintData(EndianUtils.Endianness endian, GameVersion version, Dictionary <uint, TSS.TSSEntry> inGameDic, FAMEDAT.FAMEDAT titles) { using (var characterDataStream = Stream.Duplicate()) { characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadAscii(0x40); // custom character name uint character = characterDataStream.ReadUInt32().FromEndian(endian); // character ID characterDataStream.ReadUInt32().FromEndian(endian); // level characterDataStream.ReadUInt32().FromEndian(endian); // current HP characterDataStream.ReadUInt32().FromEndian(endian); // current TP characterDataStream.ReadUInt32().FromEndian(endian); // max HP characterDataStream.ReadUInt32().FromEndian(endian); // max TP characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // EXP characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // base attack characterDataStream.ReadUInt32().FromEndian(endian); // base magic attack characterDataStream.ReadUInt32().FromEndian(endian); // base def characterDataStream.ReadUInt32().FromEndian(endian); // base mdef characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // base agility characterDataStream.ReadUInt32().FromEndian(endian); // luck characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute fire characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute earth characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute wind characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute water characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute light characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute dark characterDataStream.ReadUInt32().FromEndian(endian); // base attack attribute physical...? characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier fire characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier earth characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier wind characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier water characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier light characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier dark characterDataStream.ReadUInt32().FromEndian(endian); // base damage multiplier physical? characterDataStream.DiscardBytes(0xA8A60 - 0xA89F0); // ? characterDataStream.ReadUInt32().FromEndian(endian); // modified attack (base + from equipment) characterDataStream.ReadUInt32().FromEndian(endian); // mod def characterDataStream.ReadUInt32().FromEndian(endian); // mod matk characterDataStream.ReadUInt32().FromEndian(endian); // mod mdef characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // mod agility characterDataStream.ReadUInt32().FromEndian(endian); // mod luck characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // ? characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute fire characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute earth characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute wind characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute water characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute light characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute dark characterDataStream.ReadUInt32().FromEndian(endian); // mod attack attribute physical...? characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier fire characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier earth characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier wind characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier water characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier light characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier dark characterDataStream.ReadUInt32().FromEndian(endian); // mod damage multiplier physical? characterDataStream.DiscardBytes(0xA8E04 - 0xA8ABC); // ? characterDataStream.ReadUInt32().FromEndian(endian); // enemy kill counter (?) characterDataStream.DiscardBytes(0xAAE28 - 0xA8E08); // ? // skill equipment is stored around here characterDataStream.DiscardBytes(0xAC5B8 - 0xAAE28); // ? uint[] titlesUnlockedBitfield = characterDataStream.ReadUInt32Array(15, endian); foreach (var title in titles.TitleList) { bool haveTitle = ((titlesUnlockedBitfield[title.ID / 32] >> (int)(title.ID % 32)) & 1) > 0; if (haveTitle || ((title.BunnyGuildPointsMaybe > 0 || (!version.Is360() && title.ID == 67)) && title.Character == character)) { Console.WriteLine((haveTitle ? "Y" : "N") + ": " + inGameDic[title.NameStringDicID].StringEngOrJpn); } } characterDataStream.DiscardBytes(0xAC938 - 0xAC5F4); // ? } }
public SaveDataBlockPartyData(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public SaveDataBlockFieldGadget(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public DuplicatableStream DuplicateStream() { return(Stream.Duplicate()); }
public SaveDataBlockStandbyEnemy(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public SaveDataBlockTerasureSave(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public SaveDataBlockSnowBoard(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public SaveDataBlockPCStatus(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }
public SaveDataBlockFieldCamera(DuplicatableStream blockStream) { Stream = blockStream.Duplicate(); }