diff --git a/src/data/cast_roll_arrangement.bin b/src/data/cast_roll_arrangement.bin new file mode 100644 index 0000000..8a11a19 Binary files /dev/null and b/src/data/cast_roll_arrangement.bin differ diff --git a/src/data/cast_roll_graphics.bin b/src/data/cast_roll_graphics.bin new file mode 100644 index 0000000..0df41dd Binary files /dev/null and b/src/data/cast_roll_graphics.bin differ diff --git a/src/data/cast_sign_arrangements.bin b/src/data/cast_sign_arrangements.bin new file mode 100644 index 0000000..68d86ee Binary files /dev/null and b/src/data/cast_sign_arrangements.bin differ diff --git a/src/data/cast_sign_graphics.bin b/src/data/cast_sign_graphics.bin new file mode 100644 index 0000000..a7a25eb Binary files /dev/null and b/src/data/cast_sign_graphics.bin differ diff --git a/test.json b/test.json new file mode 100644 index 0000000..1679dce --- /dev/null +++ b/test.json @@ -0,0 +1,22 @@ +{ + "Renders":[ + { + "Y":10, + "Center_X":10, + "Text":"This is a test...", + "Font": 0 + }, + { + "Y":12, + "Center_X":10, + "Text":"This is a test2...", + "Font": 0 + }, + { + "Y":16, + "Center_X":30, + "Text":"\"", + "Font": 0 + } + ] +} \ No newline at end of file diff --git a/tools/M12Tools.sln b/tools/M12Tools.sln index b2e3474..62fcd10 100644 --- a/tools/M12Tools.sln +++ b/tools/M12Tools.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28407.52 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.168 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptTool", "ScriptTool\ScriptTool.csproj", "{29AB41A8-6405-4C4B-A374-BF1BD7AF1A27}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScriptToolGui", "ScriptToolGui\ScriptToolGui.csproj", "{BAE5CBBF-D0B3-4F82-A80A-957CEC379238}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RenderCastRoll", "RenderCastRoll\RenderCastRoll.csproj", "{DD1C607C-5A74-4921-81A4-6BF530A191D7}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {BAE5CBBF-D0B3-4F82-A80A-957CEC379238}.Debug|Any CPU.Build.0 = Debug|Any CPU {BAE5CBBF-D0B3-4F82-A80A-957CEC379238}.Release|Any CPU.ActiveCfg = Release|Any CPU {BAE5CBBF-D0B3-4F82-A80A-957CEC379238}.Release|Any CPU.Build.0 = Release|Any CPU + {DD1C607C-5A74-4921-81A4-6BF530A191D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DD1C607C-5A74-4921-81A4-6BF530A191D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DD1C607C-5A74-4921-81A4-6BF530A191D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DD1C607C-5A74-4921-81A4-6BF530A191D7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/RenderCastRoll/App.config b/tools/RenderCastRoll/App.config new file mode 100644 index 0000000..731f6de --- /dev/null +++ b/tools/RenderCastRoll/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tools/RenderCastRoll/Asset.cs b/tools/RenderCastRoll/Asset.cs new file mode 100644 index 0000000..21fba14 --- /dev/null +++ b/tools/RenderCastRoll/Asset.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace RenderCastRoll +{ + public static class Asset + { + public static readonly string AssetPath; + + static Asset() + { + AssetPath = AppDomain.CurrentDomain.BaseDirectory; + } + + public static string GetFullPath(string path) + => Path.Combine(AssetPath, path); + + public static string ReadAllText(string path) + => File.ReadAllText(GetFullPath(path)); + + public static byte[] ReadAllBytes(string path) + => File.ReadAllBytes(GetFullPath(path)); + + public static string[] ReadAllLines(string path) + => File.ReadAllLines(GetFullPath(path)); + + public static void WriteAllText(string path, string text) + => File.WriteAllText(GetFullPath(path), text); + } +} diff --git a/tools/RenderCastRoll/Program.cs b/tools/RenderCastRoll/Program.cs new file mode 100644 index 0000000..a34eec9 --- /dev/null +++ b/tools/RenderCastRoll/Program.cs @@ -0,0 +1,275 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace RenderCastRoll +{ + class Program + { + static IDictionary m12CharByteLookup; + static byte[] BitsToNybbleLookup; + static readonly ushort Palette = 0xF000; + static RenderFont[] Fonts; + static byte[] Graphics; + static _1bppTile emptyTile = new _1bppTile(); + static _1bppTile[] _1bppGraphics; + static _1bppTile[] _1bppGraphics_RotX; + static _1bppTile[] _1bppGraphics_RotY; + static _1bppTile[] _1bppGraphics_RotXY; + static ushort[] Arrangements; + static readonly ushort arrStart = 0x100; + + static void Main(string[] args) + { + if (args.Length != 2) + return; + + //Initialization + string rendersJson = File.ReadAllText(args[0]); + string dataFolder = args[1] + Path.DirectorySeparatorChar; + Fonts = new RenderFont[2]; + Fonts[0] = new RenderFont(dataFolder, "main"); + Fonts[1] = new RenderFont(dataFolder, "saturn"); + byte[] CastGraphics = File.ReadAllBytes(dataFolder + "cast_sign_graphics.bin"); + byte[] CastArrangements = File.ReadAllBytes(dataFolder + "cast_sign_arrangements.bin"); + List renders = JsonConvert.DeserializeObject(rendersJson).Renders; + BitsToNybbleLookup = Asset.ReadAllBytes("bits_to_nybbles.bin"); + m12CharByteLookup = JsonConvert.DeserializeObject>(Asset.ReadAllText("m12-byte-lookup.json")); + Graphics = new byte[0x6000]; + Arrangements = new ushort[0x4E80]; + + for (int i = 0; i < Arrangements.Length; i++) + Arrangements[i] = 0x3FF; //Empty tile + + for (int i = 0; i < CastGraphics.Length; i++) + Graphics[0x6000 - CastGraphics.Length + i] = CastGraphics[i]; //Put the CAST graphics in + + int castArrPos = readIntLE(CastArrangements, 0); //First 4 bytes are the position of the CAST arrangements + for (int i = 0; i < ((CastArrangements.Length - 4) >> 1); i++) //Put the CAST arrangements in + { + Arrangements[(castArrPos / 2) + i] = readUShortLE(CastArrangements, (i * 2) + 4); + } + + int maxTiles = 0x300 - (CastGraphics.Length / 0x20); + _1bppGraphics = new _1bppTile[maxTiles]; + _1bppGraphics_RotX = new _1bppTile[maxTiles]; + _1bppGraphics_RotY = new _1bppTile[maxTiles]; + _1bppGraphics_RotXY = new _1bppTile[maxTiles]; + int UsedTiles = 0; + WritingBuffer[] buffers = new WritingBuffer[renders.Count]; + + //Render the text as 1bpp + for (int i = 0; i < renders.Count; i++) + buffers[i] = renderText(renders[i]); + + for (int i = 0; i < buffers.Length; i++) + UsedTiles = insertInGraphics(buffers[i], UsedTiles); + + //Put the arrangements in + for (int i = 0; i < renders.Count; i++) + { + int pos = buffers[i].startPos + 1 + (renders[i].Y * 0x20); //The + 1 is here because the scene's map starts from tile 1. Not tile 0 + for (int j = 0; j < buffers[i].used; j++) + { + Arrangements[pos + j] = buffers[i].arrangements[0, j]; + Arrangements[pos + 0x20 + j] = buffers[i].arrangements[1, j]; + } + } + + //Convert the 1bpp tiles to 4bpp + for (int tile = 0; tile < UsedTiles; tile++) + { + int basePos = tile * 0x20; + _1bppTile pre_converted_tile = _1bppGraphics[tile]; + for (int i = 0; i < 8; i++) + { + int row = BitsToNybbleLookup[pre_converted_tile.getRow(i)]; + for (int j = 0; j < 4; j++) + Graphics[basePos + (i * 4) + j] = (byte)((row >> (j * 8)) & 0xFF); + } + } + + File.WriteAllBytes(dataFolder + "cast_roll_graphics.bin", Graphics); + File.WriteAllBytes(dataFolder + "cast_roll_arrangement.bin", convertUShortArrToByteLE(Arrangements)); + } + + static int readIntLE(byte[] arr, int pos) + { + return arr[pos] + (arr[pos + 1] << 8) + (arr[pos + 2] << 16) + (arr[pos + 3] << 24); + } + + static ushort readUShortLE(byte[] arr, int pos) + { + return (ushort)(arr[pos] + (arr[pos + 1] << 8)); + } + + static byte[] convertUShortArrToByteLE(ushort[] arr) + { + byte[] newArr = new byte[arr.Length * 2]; + for (int i = 0; i < arr.Length; i++) + { + newArr[(i * 2)] = (byte)((arr[i]) & 0xFF); + newArr[(i * 2) + 1] = (byte)((arr[i] >> 8) & 0xFF); + } + return newArr; + } + + static int insertInGraphics(WritingBuffer buf, int usedInGraphics) + { + //Optimally inserts the graphics inside the final product + int total = usedInGraphics; + + for (int i = 0; i < buf.used; i++) + { + for (int j = 0; j < 2; j++) + { + _1bppTile tile = buf.tiles[j, i]; + _1bppTile rotatedXTile = null; + _1bppTile rotatedYTile = null; + _1bppTile rotatedXYTile = null; + + int rot = 0; + int pos = -1; + if (emptyTile.Equals(tile)) + pos = 0x2FF; //Empty tile + else + pos = getPosInFinal(tile, total, _1bppGraphics); + + if (pos == -1) + { + rot = 1; + rotatedXTile = tile.rotateX(); + pos = getPosInFinal(rotatedXTile, total, _1bppGraphics_RotX); + + if (pos == -1) + { + rot = 2; + rotatedYTile = tile.rotateY(); + pos = getPosInFinal(rotatedYTile, total, _1bppGraphics_RotY); + + if (pos == -1) + { + rotatedXYTile = rotatedXTile.rotateY(); + rot = 3; + pos = getPosInFinal(rotatedXYTile, total, _1bppGraphics_RotXY); + } + } + } + + if (pos == -1) //Hasn't been found in any of the ways the buffer can be looked at + { + rot = 0; + pos = total++; + _1bppGraphics[pos] = tile; //If we're here, we already calculated all four of them + _1bppGraphics_RotX[pos] = rotatedXTile; + _1bppGraphics_RotY[pos] = rotatedYTile; + _1bppGraphics_RotXY[pos] = rotatedXYTile; + } + + buf.arrangements[j, i] = (ushort)(Palette | (pos + arrStart) | (rot << 0xA)); + } + } + + return total; + } + + static int getPosInFinal(_1bppTile tile, int total, _1bppTile[] finalProd) + { + int pos = -1; + for (int k = 0; k < total; k++) + if (finalProd[k].Equals(tile)) + { + pos = k; + break; + } + return pos; + } + + static WritingBuffer renderText(Render r) + { + WritingBuffer a = new WritingBuffer(); + byte[] text = getTextBytes(r.Text); + int len = getTextLength(text, r.Font); + int x = r.Center_X - (len / 2); + if (x < 0) + x = 0; + a.startPos = (x >> 3); + int bufferPos = x & 7; + + for (int i = 0; i < text.Length; i++) + bufferPos += _1bppRenderChar(text[i], bufferPos, r.Font, a); + + a.used = (bufferPos + 7) >> 3; + return a; + } + + static byte _1bppRenderChar(byte chr, int x, int font, WritingBuffer buf) + { + //Renders a character + int tileHeight = 2; + int tileWidth = 2; + int tileX = x >> 3; + int chrPos = chr * tileWidth * tileHeight * 8; + int offsetX = x & 7; + byte vWidth = Fonts[font].fontWidth[chr * 2]; + byte rWidth = Fonts[font].fontWidth[(chr * 2) + 1]; + for(int dTileY = 0; dTileY < tileHeight; dTileY++) + { + int dTileX = 0; + int renderedWidth = rWidth; + while (renderedWidth > 0) + { + int tileIndexX = tileX + dTileX; + _1bppTile leftTile = buf.tiles[dTileY, tileIndexX]; + _1bppTile rightTile = buf.tiles[dTileY, tileIndexX + 1]; + + for (int row = 0; row < 8; row++) + { + ushort canvasRow = (ushort)(leftTile.getRow(row) | (rightTile.getRow(row) << 8)); + ushort glyphRow = (ushort)(Fonts[font].font[chrPos + row + (((dTileY * tileWidth) + dTileX) * 8)] << offsetX); + + canvasRow |= glyphRow; + leftTile.setRow(row, (byte)(canvasRow & 0xFF)); + rightTile.setRow(row, (byte)((canvasRow >> 8) & 0xFF)); + } + + renderedWidth -= 8; + dTileX++; + } + } + return vWidth; + } + + static byte[] getTextBytes(String str) + { + //Reads a string and converts it to bytes + List tokens = new List(); + for (int i = 0; str.Length > 0; i++) + { + string token = str[0].ToString(); + str = str.Substring(1); + if (token == "[") + while (str.Length > 0 && !token.EndsWith("]")) + { + token += str[0].ToString(); + str = str.Substring(1); + } + if (m12CharByteLookup.ContainsKey(token)) + tokens.Add(m12CharByteLookup[token]); + } + return tokens.ToArray(); + } + + static int getTextLength(byte[] text, int Font) + { + int len = 0; + for (int i = 0; i < text.Length; i++) + len += Fonts[Font].fontWidth[2 * text[i]]; + return len; + } + } +} diff --git a/tools/RenderCastRoll/Properties/AssemblyInfo.cs b/tools/RenderCastRoll/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..662d477 --- /dev/null +++ b/tools/RenderCastRoll/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// Le informazioni generali relative a un assembly sono controllate dal seguente +// set di attributi. Modificare i valori di questi attributi per modificare le informazioni +// associate a un assembly. +[assembly: AssemblyTitle("RenderCastRoll")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("HP Inc.")] +[assembly: AssemblyProduct("RenderCastRoll")] +[assembly: AssemblyCopyright("Copyright © HP Inc. 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Se si imposta ComVisible su false, i tipi in questo assembly non saranno visibili +// ai componenti COM. Se è necessario accedere a un tipo in questo assembly da +// COM, impostare su true l'attributo ComVisible per tale tipo. +[assembly: ComVisible(false)] + +// Se il progetto viene esposto a COM, il GUID seguente verrà utilizzato come ID della libreria dei tipi +[assembly: Guid("dd1c607c-5a74-4921-81a4-6bf530a191d7")] + +// Le informazioni sulla versione di un assembly sono costituite dai seguenti quattro valori: +// +// Versione principale +// Versione secondaria +// Numero di build +// Revisione +// +// È possibile specificare tutti i valori oppure impostare valori predefiniti per i numeri relativi alla revisione e alla build +// usando l'asterisco '*' come illustrato di seguito: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tools/RenderCastRoll/RenderCastRoll.csproj b/tools/RenderCastRoll/RenderCastRoll.csproj new file mode 100644 index 0000000..9daf848 --- /dev/null +++ b/tools/RenderCastRoll/RenderCastRoll.csproj @@ -0,0 +1,65 @@ + + + + + Debug + AnyCPU + {DD1C607C-5A74-4921-81A4-6BF530A191D7} + Exe + RenderCastRoll + RenderCastRoll + v4.6.1 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + + + Always + + + Always + + + + + \ No newline at end of file diff --git a/tools/RenderCastRoll/RenderTools.cs b/tools/RenderCastRoll/RenderTools.cs new file mode 100644 index 0000000..5be027d --- /dev/null +++ b/tools/RenderCastRoll/RenderTools.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace RenderCastRoll +{ + public class Render + { + public int Y { get; set; } + public int Center_X { get; set; } + public string Text { get; set; } + public int Font { get; set; } + } + + public class RenderRoot + { + public List Renders { get; set; } + } + + public class RenderFont + { + public byte[] font; + public byte[] fontWidth; + + public RenderFont(string folder, string fontName) + { + font = File.ReadAllBytes(folder + "m2-font-" + fontName + ".bin"); + fontWidth = File.ReadAllBytes(folder + "m2-widths-" + fontName + ".bin"); + } + } + + public class _1bppTile + { + UInt64 tile; + + public _1bppTile() : this(0) { } + + public _1bppTile(UInt64 val) + { + tile = val; + } + + public byte getRow(int i) + { + return (byte)((tile >> i) & 0xFF); + } + + public void setRow(int i, byte val) + { + UInt64 mask = ~((UInt64)(0xFF) << i); + tile = (tile & mask) | ((UInt64)val << i); + } + + public UInt64 getColumn(int i) + { + UInt64 mask = (UInt64)(0x0101010101010101 << i); + return mask & tile; + } + + public void setColumn(int i, UInt64 val) + { + UInt64 mask = ~(UInt64)(0x0101010101010101 << i); + tile = (tile & mask) | val; + } + + public bool Equals(_1bppTile t) + { + return this.tile == t.tile; + } + + public _1bppTile rotateX() + { + _1bppTile newTile = new _1bppTile(tile); + for (int row = 0; row < 8; row++) + { + byte val = newTile.getRow(row); + byte newVal = 0; + for (int i = 0; i < 8; i++) + newVal |= (byte)(((val >> i) & 1) << (7 - i)); + newTile.setRow(row, newVal); + } + return newTile; + } + + public _1bppTile rotateY() + { + _1bppTile newTile = new _1bppTile(tile); + for (int column = 0; column < 8; column++) + { + UInt64 val = newTile.getColumn(column); + UInt64 newVal = 0; + for (int i = 0; i < 8; i++) + newVal |= ((val >> (i * 8)) & 0xFF) << ((7 - i) * 8); + newTile.setColumn(column, newVal); + } + return newTile; + } + } + + public class WritingBuffer + { + public _1bppTile[,] tiles; + public ushort[,] arrangements; + public int used; + public int startPos; + + public WritingBuffer() + { + used = 0; + startPos = 0; + tiles = new _1bppTile[2, 0x20]; + arrangements = new ushort[2, 0x20]; + for (int i = 0; i < 2; i++) + for (int j = 0; j < 0x20; j++) + { + tiles[i, j] = new _1bppTile(); + arrangements[i, j] = 0x3FF; //Empty tile + } + } + } +} diff --git a/tools/RenderCastRoll/bits_to_nybbles.bin b/tools/RenderCastRoll/bits_to_nybbles.bin new file mode 100644 index 0000000..3508803 Binary files /dev/null and b/tools/RenderCastRoll/bits_to_nybbles.bin differ diff --git a/tools/RenderCastRoll/m12-byte-lookup.json b/tools/RenderCastRoll/m12-byte-lookup.json new file mode 100644 index 0000000..bc39cdc --- /dev/null +++ b/tools/RenderCastRoll/m12-byte-lookup.json @@ -0,0 +1,98 @@ +{ + " ": 0, + "!": 1, + "\"": 2, + "#": 3, + "$": 4, + "%": 5, + "&": 6, + "'": 7, + "(": 8, + ")": 9, + "*": 10, + "+": 11, + ",": 12, + "-": 13, + ".": 14, + "/": 15, + "0": 16, + "1": 17, + "2": 18, + "3": 19, + "4": 20, + "5": 21, + "6": 22, + "7": 23, + "8": 24, + "9": 25, + ":": 26, + ";": 27, + "<": 28, + "=": 29, + ">": 30, + "?": 31, + "@": 32, + "A": 33, + "B": 34, + "C": 35, + "D": 36, + "E": 37, + "F": 38, + "G": 39, + "H": 40, + "I": 41, + "J": 42, + "K": 43, + "L": 44, + "M": 45, + "N": 46, + "O": 47, + "P": 47, + "Q": 49, + "R": 50, + "S": 51, + "T": 52, + "U": 53, + "V": 54, + "W": 55, + "X": 56, + "Y": 57, + "Z": 58, + "[8B]": 59, + "[8C]": 60, + "[8D]": 61, + "[8E]": 62, + "[8F]": 63, + "`": 64, + "a": 65, + "b": 66, + "c": 67, + "d": 68, + "e": 69, + "f": 70, + "g": 71, + "h": 72, + "i": 73, + "j": 74, + "k": 75, + "l": 76, + "m": 77, + "n": 78, + "o": 79, + "p": 80, + "q": 81, + "r": 82, + "s": 83, + "t": 84, + "u": 85, + "v": 86, + "w": 87, + "x": 88, + "y": 89, + "z": 90, + "{": 91, + "|": 92, + "}": 93, + "~": 94, + "[AF]": 95 +} \ No newline at end of file diff --git a/tools/RenderCastRoll/packages.config b/tools/RenderCastRoll/packages.config new file mode 100644 index 0000000..8ab13a9 --- /dev/null +++ b/tools/RenderCastRoll/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file