Create Cast Roll pre-rendering software

This commit is contained in:
Lorenzo Carletti 2020-08-23 10:59:40 +02:00
parent 437ee654f5
commit 3d184a0280
15 changed files with 670 additions and 2 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

22
test.json Normal file
View File

@ -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
}
]
}

View File

@ -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

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

View File

@ -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);
}
}

View File

@ -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<string, byte> 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<Render> renders = JsonConvert.DeserializeObject<RenderRoot>(rendersJson).Renders;
BitsToNybbleLookup = Asset.ReadAllBytes("bits_to_nybbles.bin");
m12CharByteLookup = JsonConvert.DeserializeObject<Dictionary<string, byte>>(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<byte> tokens = new List<byte>();
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;
}
}
}

View File

@ -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")]

View File

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DD1C607C-5A74-4921-81A4-6BF530A191D7}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>RenderCastRoll</RootNamespace>
<AssemblyName>RenderCastRoll</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Asset.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RenderTools.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="bits_to_nybbles.bin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="m12-byte-lookup.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -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<Render> 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
}
}
}
}

Binary file not shown.

View File

@ -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
}

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.3" targetFramework="net461" />
</packages>