using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; namespace FF8 { /// /// class to add function to dictionary /// /// public static class DictionaryEx { #region Methods /// /// Reverses Key and Value of dictionary. /// /// /// /// /// public static Dictionary Reverse(this IDictionary source) { Dictionary dictionary = new Dictionary(); foreach (KeyValuePair entry in source) { if (!dictionary.ContainsKey(entry.Value)) dictionary.Add(entry.Value, entry.Key); } return dictionary; } #endregion Methods } /// /// This holds the encoded bytes and provides implict casts to string and byte[] /// public class FF8String : IEnumerator, IEnumerable { #region Fields private byte[] value; private int position = 0; #endregion Fields #region Constructors public FF8String() { } public FF8String(byte[] @value) => Value = @value; public FF8String(string @value) => Value = Memory.DirtyEncoding.GetBytes(@value); #endregion Constructors #region Properties public byte[] Value { get => value; set => this.value = value; } public string Value_str => ToString(); public int Length => value==null? 0:value.Length; public object Current { get => Value[position-1] ; } #endregion Properties #region Indexers public byte this[int index] => value[index]; #endregion Indexers #region Methods public static implicit operator byte[] (FF8String input) => input?.Value; public static implicit operator FF8String(string input) => new FF8String(input); public static implicit operator FF8String(byte[] input) => new FF8String(input); public static implicit operator string(FF8String input) => input?.ToString(); public override string ToString() => Memory.DirtyEncoding.GetString(Value); public bool MoveNext() => ++position <= Length; public void Reset() => position = 0; public IEnumerator GetEnumerator() => this; public FF8String Append(FF8String end) { if (end != null && end.Length > 0) { Array.Resize(ref value, Length + end.Length); Array.Copy(end, 0, value, Length, end.Length); } return this; } public static FF8String Combine(FF8String start,FF8String end) { if (end != null && end.Length > 0) { byte[] combine = new byte[start.Length + end.Length]; Array.Copy(start, 0, combine, 0, start.Length); Array.Copy(end, 0, combine, start.Length, end.Length); return combine; } else return start; } public FF8String ReplaceRegion() { int i = 0; do { i = Array.FindIndex(value, i, Length - i, x => x == 0x0E); if (i >= 0) { byte id = (byte)(value[i + 1] - 0x20); //byte[] start = value.Take(i).ToArray(); byte[] newdata = Memory.Strings.Read(Strings.FileID.NAMEDIC, 0, id); byte[] end = value.Skip(2 + i).ToArray(); Array.Resize(ref value, Length + newdata.Length - 2); Array.Copy(newdata,0,value,i,newdata.Length); Array.Copy(end, 0, value, i+newdata.Length, end.Length); i+=newdata.Length; } } while (i >= 0&& i < Length); return this; } #endregion Methods } /// /// Goal is to improve conversion from FF8 byte[] to string and back. possible 0 to 4 ratio on /// special chracters. /// internal class DirtyEncoding : Encoding { #region Fields public static readonly Dictionary BytetoChar = new Dictionary { //Commented out special bytes so they are passed through and I can see them in the dump file. Then I can figure out what to do with them. {0x00, '\0'},// pos:-32, col:0, row:-1 -- is end of a string. MSG files have more than one string sepperated by \0 //{0x01, ''},// pos:-31, col:0, row:0 -- //some strings start with 0x01. might mark correct answer on SeeD exams. {0x02, '\n'},// pos:-30, col:0, row:0 -- new line //{0x03, ''},// pos:-29, col:0, row:0 -- special character. {0x03,0x40 = [Angelo's Name]} //{0x04, ''},// pos:-28, col:0, row:0 -- //{0x05, ''},// pos:-27, col:0, row:0 -- //{0x06, ''},// pos:-26, col:0, row:0 -- //{0x07, ''},// pos:-25, col:0, row:0 -- //{0x08, ''},// pos:-24, col:0, row:0 -- //{0x09, ''},// pos:-23, col:0, row:0 -- //{0x0A, ''},// pos:-22, col:0, row:0 -- //{0x0B, ''},// pos:-21, col:0, row:0 -- //{0x0C, ''},// pos:-20, col:0, row:0 -- //{0x0D, ''},// pos:-19, col:0, row:0 -- //{0x0E, ''},// pos:-18, col:0, row:0 -- <$> //{0x0F, ''},// pos:-17, col:0, row:0 -- //{0x10, ''},// pos:-16, col:0, row:0 -- //{0x11, ''},// pos:-15, col:0, row:0 -- //{0x12, ''},// pos:-14, col:0, row:0 -- //{0x13, ''},// pos:-13, col:0, row:0 -- //{0x14, ''},// pos:-12, col:0, row:0 -- //{0x15, ''},// pos:-11, col:0, row:0 -- //{0x16, ''},// pos:-10, col:0, row:1 -- //{0x17, ''},// pos:-9, col:0, row:0 -- //{0x18, ''},// pos:-8, col:0, row:0 -- //{0x19, ''},// pos:-7, col:0, row:0 -- //{0x1A, ''},// pos:-6, col:0, row:0 -- //{0x1B, ''},// pos:-5, col:0, row:0 -- //{0x1C, ''},// pos:-4, col:0, row:0 -- //{0x1D, ''},// pos:-3, col:0, row:0 -- //{0x1E, ''},// pos:-2, col:0, row:0 -- //{0x1F, ''},// pos:-1, col:0, row:0 -- {0x20, ' '},// pos:0, col:1, row:1 -- Start of font texture {0x21, '0'},// pos:1, col:2, row:1 -- {0x22, '1'},// pos:2, col:3, row:1 -- {0x23, '2'},// pos:3, col:4, row:1 -- {0x24, '3'},// pos:4, col:5, row:1 -- {0x25, '4'},// pos:5, col:6, row:1 -- {0x26, '5'},// pos:6, col:7, row:1 -- {0x27, '6'},// pos:7, col:8, row:1 -- {0x28, '7'},// pos:8, col:9, row:1 -- {0x29, '8'},// pos:9, col:10, row:1 -- {0x2A, '9'},// pos:10, col:11, row:1 -- {0x2B, '%'},// pos:11, col:12, row:2 -- {0x2C, '/'},// pos:12, col:13, row:2 -- {0x2D, ':'},// pos:13, col:14, row:2 -- {0x2E, '!'},// pos:14, col:15, row:2 -- {0x2F, '?'},// pos:15, col:16, row:2 -- {0x30, '…'},// pos:16, col:17, row:2 -- {0x31, '+'},// pos:17, col:18, row:2 -- {0x32, '-'},// pos:18, col:19, row:2 -- {0x33, '='},// pos:19, col:20, row:2 -- {0x34, '*'},// pos:20, col:21, row:2 -- {0x35, '&'},//& pos:21, col:1, row:2 -- temporarly set to this so i could have formatting on pastebin {0x36, '「'},// pos:22, col:2, row:2 -- {0x37, '」'},// pos:23, col:3, row:2 -- {0x38, '('},// pos:24, col:4, row:2 -- {0x39, ')'},// pos:25, col:5, row:2 -- {0x3A, '·'},// pos:26, col:6, row:2 -- {0x3B, '.'},// pos:27, col:7, row:2 -- {0x3C, ','},// pos:28, col:8, row:2 -- {0x3D, '~'},// pos:29, col:9, row:2 -- {0x3E, '“'},// pos:30, col:10, row:2 -- {0x3F, '”'},// pos:31, col:11, row:2 -- {0x40, '\''},// pos:32, col:12, row:3 -- {0x41, '#'},// pos:33, col:13, row:3 -- {0x42, '$'},// pos:34, col:14, row:3 -- {0x43, '`'},// pos:35, col:15, row:3 -- {0x44, '_'},// pos:36, col:16, row:3 -- {0x45, 'A'},// pos:37, col:17, row:3 -- {0x46, 'B'},// pos:38, col:18, row:3 -- {0x47, 'C'},// pos:39, col:19, row:3 -- {0x48, 'D'},// pos:40, col:20, row:3 -- {0x49, 'E'},// pos:41, col:21, row:3 -- {0x4A, 'F'},// pos:42, col:1, row:3 -- {0x4B, 'G'},// pos:43, col:2, row:3 -- {0x4C, 'H'},// pos:44, col:3, row:3 -- {0x4D, 'I'},// pos:45, col:4, row:3 -- {0x4E, 'J'},// pos:46, col:5, row:3 -- {0x4F, 'K'},// pos:47, col:6, row:3 -- {0x50, 'L'},// pos:48, col:7, row:3 -- {0x51, 'M'},// pos:49, col:8, row:3 -- {0x52, 'N'},// pos:50, col:9, row:3 -- {0x53, 'O'},// pos:51, col:10, row:3 -- {0x54, 'P'},// pos:52, col:11, row:3 -- {0x55, 'Q'},// pos:53, col:12, row:4 -- {0x56, 'R'},// pos:54, col:13, row:4 -- {0x57, 'S'},// pos:55, col:14, row:4 -- {0x58, 'T'},// pos:56, col:15, row:4 -- {0x59, 'U'},// pos:57, col:16, row:4 -- {0x5A, 'V'},// pos:58, col:17, row:4 -- {0x5B, 'W'},// pos:59, col:18, row:4 -- {0x5C, 'X'},// pos:60, col:19, row:4 -- {0x5D, 'Y'},// pos:61, col:20, row:4 -- {0x5E, 'Z'},// pos:62, col:21, row:4 -- {0x5F, 'a'},// pos:63, col:1, row:4 -- {0x60, 'b'},// pos:64, col:2, row:4 -- {0x61, 'c'},// pos:65, col:3, row:4 -- {0x62, 'd'},// pos:66, col:4, row:4 -- {0x63, 'e'},// pos:67, col:5, row:4 -- {0x64, 'f'},// pos:68, col:6, row:4 -- {0x65, 'g'},// pos:69, col:7, row:4 -- {0x66, 'h'},// pos:70, col:8, row:4 -- {0x67, 'i'},// pos:71, col:9, row:4 -- {0x68, 'j'},// pos:72, col:10, row:4 -- {0x69, 'k'},// pos:73, col:11, row:4 -- {0x6A, 'l'},// pos:74, col:12, row:5 -- {0x6B, 'm'},// pos:75, col:13, row:5 -- {0x6C, 'n'},// pos:76, col:14, row:5 -- {0x6D, 'o'},// pos:77, col:15, row:5 -- {0x6E, 'p'},// pos:78, col:16, row:5 -- {0x6F, 'q'},// pos:79, col:17, row:5 -- {0x70, 'r'},// pos:80, col:18, row:5 -- {0x71, 's'},// pos:81, col:19, row:5 -- {0x72, 't'},// pos:82, col:20, row:5 -- {0x73, 'u'},// pos:83, col:21, row:5 -- {0x74, 'v'},// pos:84, col:1, row:5 -- {0x75, 'w'},// pos:85, col:2, row:5 -- {0x76, 'x'},// pos:86, col:3, row:5 -- {0x77, 'y'},// pos:87, col:4, row:5 -- {0x78, 'z'},// pos:88, col:5, row:5 -- {0x79, 'À'},// pos:89, col:6, row:5 -- {0x7A, 'Á'},// pos:90, col:7, row:5 -- {0x7B, 'Â'},// pos:91, col:8, row:5 -- {0x7C, 'Ä'},// pos:92, col:9, row:5 -- {0x7D, 'Ç'},// pos:93, col:10, row:5 -- {0x7E, 'È'},// pos:94, col:11, row:5 -- {0x7F, 'É'},// pos:95, col:12, row:6 -- {0x80, 'Ê'},// pos:96, col:13, row:6 -- {0x81, 'Ë'},// pos:97, col:14, row:6 -- {0x82, 'Ì'},// pos:98, col:15, row:6 -- {0x83, 'Í'},// pos:99, col:16, row:6 -- {0x84, 'Î'},// pos:100, col:17, row:6 -- {0x85, 'Ï'},// pos:101, col:18, row:6 -- {0x86, 'Ñ'},// pos:102, col:19, row:6 -- {0x87, 'Ò'},// pos:103, col:20, row:6 -- {0x88, 'Ó'},// pos:104, col:21, row:6 -- {0x89, 'Ô'},// pos:105, col:1, row:6 -- {0x8A, 'Ö'},// pos:106, col:2, row:6 -- {0x8B, 'Ù'},// pos:107, col:3, row:6 -- {0x8C, 'Ú'},// pos:108, col:4, row:6 -- {0x8D, 'Û'},// pos:109, col:5, row:6 -- {0x8E, 'Ü'},// pos:110, col:6, row:6 -- {0x8F, 'Œ'},// pos:111, col:7, row:6 -- {0x90, 'Ʀ'},// pos:112, col:8, row:6 -- {0x91, 'à'},// pos:113, col:9, row:6 -- {0x92, 'á'},// pos:114, col:10, row:6 -- {0x93, 'â'},// pos:115, col:11, row:6 -- {0x94, 'ä'},// pos:116, col:12, row:7 -- {0x95, 'ç'},// pos:117, col:13, row:7 -- {0x96, 'è'},// pos:118, col:14, row:7 -- {0x97, 'é'},// pos:119, col:15, row:7 -- {0x98, 'ê'},// pos:120, col:16, row:7 -- {0x99, 'ë'},// pos:121, col:17, row:7 -- {0x9A, 'ì'},// pos:122, col:18, row:7 -- {0x9B, 'í'},// pos:123, col:19, row:7 -- {0x9C, 'î'},// pos:124, col:20, row:7 -- {0x9D, 'ï'},// pos:125, col:21, row:7 -- {0x9E, 'ñ'},// pos:126, col:1, row:7 -- {0x9F, 'ò'},// pos:127, col:2, row:7 -- {0xA0, 'ó'},// pos:128, col:3, row:7 -- {0xA1, 'ô'},// pos:129, col:4, row:7 -- {0xA2, 'ö'},// pos:130, col:5, row:7 -- {0xA3, 'ù'},// pos:131, col:6, row:7 -- {0xA4, 'ú'},// pos:132, col:7, row:7 -- {0xA5, 'û'},// pos:133, col:8, row:7 -- {0xA6, 'ü'},// pos:134, col:9, row:7 -- {0xA7, 'œ'},// pos:135, col:10, row:7 -- {0xA8, 'Ⅷ'},// pos:136, col:11, row:7 -- {0xA9, '['},// pos:137, col:12, row:8 -- {0xAA, ']'},// pos:138, col:13, row:8 -- {0xAB, '⬛'},// pos:139, col:14, row:8 -- {0xAC, '⦾'},// pos:140, col:15, row:8 -- {0xAD, '◆'},// pos:141, col:16, row:8 -- {0xAE, '【'},// pos:142, col:17, row:8 -- {0xAF, '】'},// pos:143, col:18, row:8 -- {0xB0, '⬜'},// pos:144, col:19, row:8 -- {0xB1, '★'},// pos:145, col:20, row:8 -- {0xB2, '『'},// pos:146, col:21, row:8 -- {0xB3, '』'},// pos:147, col:1, row:8 -- {0xB4, '▽'},// pos:148, col:2, row:8 -- {0xB5, ';'},// pos:149, col:3, row:8 -- {0xB6, '▼'},// pos:150, col:4, row:8 -- {0xB7, '‾'},// pos:151, col:5, row:8 -- {0xB8, '×'},// pos:152, col:6, row:8 -- {0xB9, '☆'},// pos:153, col:7, row:8 -- {0xBA, '’'},// pos:154, col:8, row:8 -- {0xBB, '↓'},// pos:155, col:9, row:8 -- {0xBC, '°'},// pos:156, col:10, row:8 -- {0xBD, '¡'},// pos:157, col:11, row:8 -- {0xBE, '¿'},// pos:158, col:12, row:9 -- // pc version sysfld00 and 01 has "Slo" here so there isn't a upside down questionmark on highres. {0xBF, '—'},// pos:159, col:13, row:9 -- {0xC0, '«'},// pos:160, col:14, row:9 -- {0xC1, '»'},// pos:161, col:15, row:9 -- {0xC2, '±'},// pos:162, col:16, row:9 -- {0xC3, '♫'},// pos:163, col:17, row:9 -- //{0xC4, ''},// pos:164, col:18, row:9 -- seems to be used as an alignment or a place holder many strings have 3 of these in a row. {0xC5, '↑'},// pos:165, col:19, row:9 -- {0xC8, '¡'},// pos:168, col:1, row:9 -- {0xC9, '™'},// pos:169, col:2, row:9 -- {0xCA, '<'},// pos:170, col:3, row:9 -- {0xCB, '>'},// pos:171, col:4, row:9 -- {0xE2, '®'},// pos:194, col:6, row:10 -- End of font texture }; /// /// multi characters bytes and double character bytes /// public static readonly Dictionary BytetoStr = new Dictionary { //{0x01, "" }, {0xC6, "VI"},// pos:166, col:20, row:9 -- {0xC7, "II"},// pos:167, col:21, row:9 -- //pc version sysfld00 and 01 {0xCC, "GA"},// pos:172, col:5, row:9 -- {0xCD, "ME"},// pos:173, col:6, row:9 -- {0xCE, "FO"},// pos:174, col:7, row:9 -- {0xCF, "LD"},// pos:175, col:8, row:9 -- {0xD0, "ER"},// pos:176, col:9, row:9 -- ////original texture - sysfont //{0xCC, "ME"},// pos:172, col:5, row:9 -- //{0xCD, "MO"},// pos:173, col:6, row:9 -- //{0xCE, "RY"},// pos:174, col:7, row:9 -- //{0xCF, "CA"},// pos:175, col:8, row:9 -- //{0xD0, "RD"},// pos:176, col:9, row:9 -- {0xD1, "Sl"},// pos:177, col:10, row:9 -- {0xD2, "ot"},// pos:178, col:11, row:9 -- {0xD3, "ing"},// pos:179, col:12, row:10 -- {0xD4, "St"},// pos:180, col:13, row:10 -- {0xD5, "ec"},// pos:181, col:14, row:10 -- {0xD6, "kp"},// pos:182, col:15, row:10 -- {0xD7, "la"},// pos:183, col:16, row:10 -- {0xD8, ":z"},// pos:184, col:17, row:10 -- {0xD9, "Fr"},// pos:185, col:18, row:10 -- {0xDA, "nt"},// pos:186, col:19, row:10 -- {0xDB, "elng"},// pos:187, col:20, row:10 -- {0xDC, "re"},// pos:188, col:21, row:10 -- {0xDD, "S:"},// pos:189, col:1, row:10 -- {0xDE, "so"},// pos:190, col:2, row:10 -- {0xDF, "Ra"},// pos:191, col:3, row:10 -- {0xE0, "nu"},// pos:192, col:4, row:10 -- {0xE1, "ra"},// pos:193, col:5, row:10 -- //{0xE3, ""},// pos:195, col:0, row:0 -- //{0xE4, ""},// pos:196, col:0, row:0 -- //{0xE5, ""},// pos:197, col:0, row:0 -- //{0xE6, ""},// pos:198, col:0, row:0 -- //{0xE7, ""},// pos:199, col:0, row:0 -- {0xE8, "in"},// pos:200, col:0, row:0 -- {0xE9, "e "},// pos:201, col:0, row:0 -- {0xEA, "ne"},// pos:202, col:0, row:0 -- {0xEB, "to"},// pos:203, col:0, row:0 -- {0xEC, "re"},// pos:204, col:0, row:0 -- {0xED, "HP"},// pos:205, col:0, row:0 -- {0xEE, "l "},// pos:206, col:0, row:0 -- {0xEF, "ll"},// pos:207, col:0, row:0 -- {0xF0, "GF"},// pos:208, col:0, row:0 -- {0xF1, "nt"},// pos:209, col:0, row:0 -- {0xF2, "il"},// pos:210, col:0, row:0 -- {0xF3, "o "},// pos:211, col:0, row:0 -- {0xF4, "ef"},// pos:212, col:0, row:0 -- {0xF5, "on"},// pos:213, col:0, row:0 -- {0xF6, " w"},// pos:214, col:0, row:0 -- {0xF7, " r"},// pos:215, col:0, row:0 -- {0xF8, "wi"},// pos:216, col:0, row:0 -- {0xF9, "fi"},// pos:217, col:0, row:0 -- //{0xFA, ""},// pos:218, col:0, row:0 -- {0xFB, "s "},// pos:219, col:0, row:0 -- {0xFC, "ar"},// pos:220, col:0, row:0 -- //{0xFD, ""},// pos:221, col:0, row:0 -- {0xFE, " S"},// pos:222, col:0, row:0 -- {0xFF, "ag"},// pos:223, col:0, row:0 -- }; public static readonly Dictionary ChartoByte = BytetoChar.Reverse(); public static readonly Dictionary StrtoByte = BytetoStr.Reverse(); #endregion Fields #region Constructors public DirtyEncoding() { //reverse key and value pairs for dictionarys for the reverse lookup SpecialCharacters = new byte[256 - BytetoChar.Count() - BytetoStr.Count]; int i = 0; for (byte b = byte.MinValue; b <= byte.MaxValue && i < SpecialCharacters.Length; b++) { if (!BytetoChar.ContainsKey(b) && !BytetoStr.ContainsKey(b)) SpecialCharacters[i++] = b; } } #endregion Constructors #region Properties /// /// Lists all bytes directly passed through unalterd. Functions are expected to handle or /// skip these. /// public byte[] SpecialCharacters { get; private set; } #endregion Properties //NOTE: There are some abstract members requiring you to implement or declare in this derived class. #region Methods public override int GetByteCount(char[] chars, int index, int count) => count + index <= chars.Length ? count - index : chars.Length - index; public override int GetBytes(char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex) => throw new System.NotImplementedException(); public override byte[] GetBytes(string s) { if (s == null) return null; using (MemoryStream ms = new MemoryStream(GetMaxCharCount(s.Length))) using (BinaryWriter bw = new BinaryWriter(ms)) { //Only way I could see to do MCB or DCB is to do a string.replace for each strtobyte. //So why not just only do SCB foreach (char c in s) { byte b = ChartoByte.ContainsKey(c) ? ChartoByte[c] : byte.Parse(c.ToString()); bw.Write(b); } return ms.ToArray(); } } public override int GetCharCount(byte[] bytes, int index, int count) => GetString(bytes.Skip(index).Take(count).ToArray()).Length; public override int GetChars(byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex) => throw new System.NotImplementedException(); public override int GetMaxByteCount(int charCount) => charCount; public override int GetMaxCharCount(int byteCount) => byteCount * 4; //largest multichar byte is 4; public override string GetString(byte[] bytes) { //using (MemoryStream ms = new MemoryStream(GetMaxByteCount(bytes.Length))) //using (BinaryWriter bw = new BinaryWriter(ms)) //using (StreamReader sr = new StreamReader(ms)) //{ string @out = ""; foreach (byte c in bytes) { string b = BytetoChar.ContainsKey(c) ? BytetoChar[c].ToString() : BytetoStr.ContainsKey(c) ? BytetoStr[c] : ((char)c).ToString(); //bw.Write(b); @out += b; } return @out; //ms.Seek(0, SeekOrigin.Begin); //return sr.ReadToEnd(); // } } #endregion Methods //And many other virtual (overridable) methods which you can override to implement your custom Encoding fully } }