// This program and the accompanying materials are made available under the terms of the // Eclipse Public License v2.0 which accompanies this distribution, and is available at // https://www.eclipse.org/legal/epl-v20.html // // SPDX-License-Identifier: EPL-2.0 using System; using System.Collections.Generic; using System.IO.Compression; using System.Linq; using System.Net.NetworkInformation; using System.Text; using System.Threading.Tasks; namespace Ficedula.FF7 { public class Kernel { private List _sections = new(); public IReadOnlyList Sections => _sections.AsReadOnly(); public Kernel(Stream source) { while (source.Position < source.Length) { ushort gzSize = source.ReadU16(), size = source.ReadU16(), fileType = source.ReadU16(); //TODO - use file type? var ms = new MemoryStream(); byte[] input = new byte[gzSize]; source.Read(input, 0, gzSize); new GZipStream(new MemoryStream(input), CompressionMode.Decompress).CopyTo(ms); _sections.Add(ms.ToArray()); } } } public class KernelText { private class Item { public bool Highlight { get; set; } public byte[] Data { get; set; } } private List _items = new(); public int Count => _items.Count; //EA: Name of a Character EB: Name of an Item EC: Number ED: Name of the Target EF: Name of Attack FO: Target's Letter (for enemies) public string Get(int index) { return Get(index, out _); } public string Get(int index, out bool highlight) { return Get(index, null, null, null, null, -1, out highlight); } public string Get(int index, IEnumerable charNames, IEnumerable itemNames, IEnumerable targetNames, IEnumerable attackNames, int subIndex, out bool highlight) { StringBuilder sb = new(); var item = _items[index]; highlight = item.Highlight; foreach (byte b in item.Data) { if (b == 0xff) break; switch (b) { case 0xEA: sb.Append(charNames.ElementAt(subIndex)); break; case 0xEB: sb.Append(itemNames.ElementAt(subIndex)); break; case 0xEC: sb.Append(subIndex.ToString()); break; case 0xED: sb.Append(targetNames.ElementAt(subIndex)); break; case 0xEF: sb.Append(attackNames.ElementAt(subIndex)); break; case 0xF0: sb.Append('A' + subIndex); //TODO translated! break; default: sb.Append(Text.Convert(new[] { b }, 0)); break; } } return sb.ToString(); } public KernelText(byte[] source) : this(new MemoryStream(source)) { } public KernelText(Stream source) { List offsets = new(); do { offsets.Add(source.ReadU16()); } while (source.Position < offsets[0]); foreach (int offset in offsets) { List output = new List(); source.Position = offset; byte b; do { b = (byte)source.ReadByte(); switch (b) { case 0xf9: byte data = (byte)source.ReadByte(); int len = (data >> 6) * 2 + 4, distance = data & 0x3f; long pos = source.Position; source.Position = source.Position - 2 - distance - 1; foreach (int _ in Enumerable.Range(0, len)) output.Add((byte)source.ReadByte()); source.Position = pos; break; case 0xff: break; default: output.Add(b); break; } } while (b != 0xff); bool highlight = false; if (output.ElementAtOrDefault(0) == 0xf8) { highlight = output[1] == 0x02; output.RemoveRange(0, 2); } _items.Add(new Item { Highlight = highlight, Data = output.ToArray(), }); } } } }