├── .gitattributes ├── .gitignore ├── FfntTool ├── App.config ├── ExtensionMethods.cs ├── Ffnt │ ├── FfntEntry.cs │ ├── FfntEntryHeader.cs │ ├── FfntFile.cs │ ├── FontData.cs │ ├── Glyph.cs │ └── GlyphMap.cs ├── FfntTool.csproj ├── Program.cs └── Properties │ └── AssemblyInfo.cs ├── FoxEngine.TranslationTool.sln ├── LICENSE ├── LangTool ├── App.config ├── ExtensionMethods.cs ├── Lang │ ├── LangEntry.cs │ └── LangFile.cs ├── LangTool.csproj ├── Program.cs ├── Properties │ └── AssemblyInfo.cs ├── Utility │ ├── BigEndianBinaryReader.cs │ ├── BigEndianBinaryWriter.cs │ ├── Endianess.cs │ ├── EndianessBitConverter.cs │ └── Fox.cs ├── lang_dictionary.txt └── packages.config ├── README.md └── SubpTool ├── App.config ├── ExtensionMethods.cs ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── Subp ├── SubpEntry.cs ├── SubpFile.cs ├── SubpIndex.cs ├── SubpLine.cs └── SubpTiming.cs └── SubpTool.csproj /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | [Rr]eleases/ 14 | x64/ 15 | x86/ 16 | build/ 17 | bld/ 18 | [Bb]in/ 19 | [Oo]bj/ 20 | 21 | # Roslyn cache directories 22 | *.ide/ 23 | 24 | # MSTest test Results 25 | [Tt]est[Rr]esult*/ 26 | [Bb]uild[Ll]og.* 27 | 28 | #NUNIT 29 | *.VisualState.xml 30 | TestResult.xml 31 | 32 | # Build Results of an ATL Project 33 | [Dd]ebugPS/ 34 | [Rr]eleasePS/ 35 | dlldata.c 36 | 37 | *_i.c 38 | *_p.c 39 | *_i.h 40 | *.ilk 41 | *.meta 42 | *.obj 43 | *.pch 44 | *.pdb 45 | *.pgc 46 | *.pgd 47 | *.rsp 48 | *.sbr 49 | *.tlb 50 | *.tli 51 | *.tlh 52 | *.tmp 53 | *.tmp_proj 54 | *.log 55 | *.vspscc 56 | *.vssscc 57 | .builds 58 | *.pidb 59 | *.svclog 60 | *.scc 61 | 62 | # Chutzpah Test files 63 | _Chutzpah* 64 | 65 | # Visual C++ cache files 66 | ipch/ 67 | *.aps 68 | *.ncb 69 | *.opensdf 70 | *.sdf 71 | *.cachefile 72 | 73 | # Visual Studio profiler 74 | *.psess 75 | *.vsp 76 | *.vspx 77 | 78 | # TFS 2012 Local Workspace 79 | $tf/ 80 | 81 | # Guidance Automation Toolkit 82 | *.gpState 83 | 84 | # ReSharper is a .NET coding add-in 85 | _ReSharper*/ 86 | *.[Rr]e[Ss]harper 87 | *.DotSettings.user 88 | 89 | # JustCode is a .NET coding addin-in 90 | .JustCode 91 | 92 | # TeamCity is a build add-in 93 | _TeamCity* 94 | 95 | # DotCover is a Code Coverage Tool 96 | *.dotCover 97 | 98 | # NCrunch 99 | _NCrunch_* 100 | .*crunch*.local.xml 101 | 102 | # MightyMoose 103 | *.mm.* 104 | AutoTest.Net/ 105 | 106 | # Web workbench (sass) 107 | .sass-cache/ 108 | 109 | # Installshield output folder 110 | [Ee]xpress/ 111 | 112 | # DocProject is a documentation generator add-in 113 | DocProject/buildhelp/ 114 | DocProject/Help/*.HxT 115 | DocProject/Help/*.HxC 116 | DocProject/Help/*.hhc 117 | DocProject/Help/*.hhk 118 | DocProject/Help/*.hhp 119 | DocProject/Help/Html2 120 | DocProject/Help/html 121 | 122 | # Click-Once directory 123 | publish/ 124 | 125 | # Publish Web Output 126 | *.[Pp]ublish.xml 127 | *.azurePubxml 128 | # TODO: Comment the next line if you want to checkin your web deploy settings 129 | # but database connection strings (with potential passwords) will be unencrypted 130 | *.pubxml 131 | *.publishproj 132 | 133 | # NuGet Packages 134 | *.nupkg 135 | # The packages folder can be ignored because of Package Restore 136 | **/packages/* 137 | # except build/, which is used as an MSBuild target. 138 | !**/packages/build/ 139 | # If using the old MSBuild-Integrated Package Restore, uncomment this: 140 | #!**/packages/repositories.config 141 | 142 | # Windows Azure Build Output 143 | csx/ 144 | *.build.csdef 145 | 146 | # Windows Store app package directory 147 | AppPackages/ 148 | 149 | # Others 150 | sql/ 151 | *.Cache 152 | ClientBin/ 153 | [Ss]tyle[Cc]op.* 154 | ~$* 155 | *~ 156 | *.dbmdl 157 | *.dbproj.schemaview 158 | *.pfx 159 | *.publishsettings 160 | node_modules/ 161 | 162 | # RIA/Silverlight projects 163 | Generated_Code/ 164 | 165 | # Backup & report files from converting an old project file 166 | # to a newer Visual Studio version. Backup files are not needed, 167 | # because we have git ;-) 168 | _UpgradeReport_Files/ 169 | Backup*/ 170 | UpgradeLog*.XML 171 | UpgradeLog*.htm 172 | 173 | # SQL Server files 174 | *.mdf 175 | *.ldf 176 | 177 | # Business Intelligence projects 178 | *.rdl.data 179 | *.bim.layout 180 | *.bim_*.settings 181 | 182 | # Microsoft Fakes 183 | FakesAssemblies/ 184 | -------------------------------------------------------------------------------- /FfntTool/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /FfntTool/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | 4 | namespace FfntTool 5 | { 6 | internal static class ExtensionMethods 7 | { 8 | internal static void Skip(this BinaryReader reader, int count) 9 | { 10 | reader.BaseStream.Seek(count, SeekOrigin.Current); 11 | } 12 | 13 | internal static string ReadString(this BinaryReader binaryReader, int count) 14 | { 15 | return new string(binaryReader.ReadChars(count)); 16 | } 17 | 18 | internal static void AlignRead(this Stream input, int alignment) 19 | { 20 | long alignmentRequired = input.Position%alignment; 21 | if (alignmentRequired > 0) 22 | input.Position += alignment - alignmentRequired; 23 | } 24 | 25 | internal static void WriteZeros(this BinaryWriter writer, int count) 26 | { 27 | byte[] zeros = new byte[count]; 28 | writer.Write(zeros); 29 | } 30 | 31 | internal static void AlignWrite(this BinaryWriter writer, int alignment, byte data) 32 | { 33 | long alignmentRequired = writer.BaseStream.Position%alignment; 34 | if (alignmentRequired > 0) 35 | { 36 | byte[] alignmentBytes = Enumerable.Repeat(data, (int) (alignment - alignmentRequired)).ToArray(); 37 | writer.Write(alignmentBytes, 0, alignmentBytes.Length); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/FfntEntry.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace FfntTool.Ffnt 4 | { 5 | public abstract class FfntEntry 6 | { 7 | public abstract FfntEntryHeader GetHeader(Stream outputStream); 8 | public abstract void Write(Stream outputStream); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/FfntEntryHeader.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace FfntTool.Ffnt 5 | { 6 | public class FfntEntryHeader 7 | { 8 | public const int FfntEntryHeaderSize = 12; 9 | public string FfntEntrySignature { get; set; } 10 | public int Offset { get; set; } 11 | public int Size { get; set; } 12 | 13 | public static FfntEntryHeader ReadFfntEntryHeader(Stream inputStream) 14 | { 15 | FfntEntryHeader ffntEntryHeader = new FfntEntryHeader(); 16 | ffntEntryHeader.Read(inputStream); 17 | return ffntEntryHeader; 18 | } 19 | 20 | public void Read(Stream inputStream) 21 | { 22 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Default, true); 23 | FfntEntrySignature = reader.ReadString(4); 24 | Offset = reader.ReadInt32(); 25 | Size = reader.ReadInt32(); 26 | } 27 | 28 | public FfntEntry ReadData(Stream inputStream) 29 | { 30 | inputStream.Position = Offset; 31 | FfntEntry data; 32 | switch (FfntEntrySignature) 33 | { 34 | case GlyphMap.GlyphSignature: 35 | data = GlyphMap.ReadGlyphMap(inputStream); 36 | break; 37 | case FontData.FontSignature: 38 | data = FontData.ReadFontData(inputStream); 39 | break; 40 | default: 41 | data = null; 42 | break; 43 | } 44 | return data; 45 | } 46 | 47 | public void Write(Stream outputStream) 48 | { 49 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 50 | writer.Write(Encoding.Default.GetBytes(FfntEntrySignature)); 51 | writer.Write(Offset); 52 | writer.Write(Size); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/FfntFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | 6 | namespace FfntTool.Ffnt 7 | { 8 | [XmlType("FfntFile")] 9 | public class FfntFile 10 | { 11 | private const string Signature = "FXFT"; 12 | private const short FfntHeaderSize = 10; 13 | private const short LittleEndianMagicNumber = 1; 14 | 15 | public FfntFile() 16 | { 17 | Entries = new List(); 18 | } 19 | 20 | [XmlArray("Entries")] 21 | public List Entries { get; set; } 22 | 23 | public static FfntFile ReadFfntFile(Stream inputStream) 24 | { 25 | FfntFile ffntFile = new FfntFile(); 26 | ffntFile.Read(inputStream); 27 | return ffntFile; 28 | } 29 | 30 | public void Read(Stream inputStream) 31 | { 32 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Default, true); 33 | string magicNumber = reader.ReadString(4); 34 | short endianess = reader.ReadInt16(); 35 | byte entryCount = reader.ReadByte(); 36 | reader.Skip(1); 37 | short headerSize = reader.ReadInt16(); 38 | inputStream.AlignRead(16); 39 | List ffntEntryHeaders = new List(); 40 | 41 | for (int i = 0; i < entryCount; i++) 42 | { 43 | ffntEntryHeaders.Add(FfntEntryHeader.ReadFfntEntryHeader(inputStream)); 44 | } 45 | 46 | foreach (var header in ffntEntryHeaders) 47 | { 48 | Entries.Add(header.ReadData(inputStream)); 49 | } 50 | } 51 | 52 | public void Write(Stream outputStream) 53 | { 54 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 55 | writer.Write(Encoding.Default.GetBytes(Signature)); 56 | writer.Write(LittleEndianMagicNumber); 57 | writer.Write((byte) Entries.Count); 58 | writer.WriteZeros(1); 59 | writer.Write(FfntHeaderSize); 60 | writer.AlignWrite(16, 0x00); 61 | 62 | long entryHeaderPosition = outputStream.Position; 63 | 64 | outputStream.Position += Entries.Count*FfntEntryHeader.FfntEntryHeaderSize; 65 | writer.AlignWrite(16, 0x00); 66 | 67 | List ffntEntryHeaders = new List(); 68 | 69 | foreach (var entry in Entries) 70 | { 71 | ffntEntryHeaders.Add(entry.GetHeader(outputStream)); 72 | entry.Write(outputStream); 73 | writer.AlignWrite(16, 0x00); 74 | } 75 | 76 | outputStream.Position = entryHeaderPosition; 77 | 78 | foreach (var header in ffntEntryHeaders) 79 | { 80 | header.Write(outputStream); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/FontData.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Xml.Serialization; 4 | 5 | namespace FfntTool.Ffnt 6 | { 7 | [XmlType("FontData")] 8 | public class FontData : FfntEntry 9 | { 10 | public const string FontSignature = "FTDT"; 11 | 12 | [XmlAttribute("Unknown1")] 13 | public int Unknown1 { get; set; } 14 | 15 | [XmlAttribute("Unknown2")] 16 | public int Unknown2 { get; set; } 17 | 18 | [XmlAttribute("Unknown3")] 19 | public int Unknown3 { get; set; } 20 | 21 | [XmlIgnore] 22 | public byte[] Data { get; set; } 23 | 24 | public static FontData ReadFontData(Stream inputStream) 25 | { 26 | FontData fontData = new FontData(); 27 | fontData.Read(inputStream); 28 | return fontData; 29 | } 30 | 31 | public void Read(Stream inputStream) 32 | { 33 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Default, true); 34 | Unknown1 = reader.ReadInt32(); 35 | int size = reader.ReadInt32(); 36 | Unknown2 = reader.ReadInt32(); 37 | Unknown3 = reader.ReadInt32(); 38 | Data = reader.ReadBytes(size); 39 | // TODO: Check which unknown properties are constant zero 40 | } 41 | 42 | public override void Write(Stream outputStream) 43 | { 44 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 45 | writer.AlignWrite(16, 0x00); 46 | writer.Write(Unknown1); 47 | writer.Write(Data.Length); 48 | writer.Write(Unknown2); 49 | writer.Write(Unknown3); 50 | writer.Write(Data); 51 | } 52 | 53 | public override FfntEntryHeader GetHeader(Stream outputStream) 54 | { 55 | FfntEntryHeader header = new FfntEntryHeader 56 | { 57 | FfntEntrySignature = FontSignature, 58 | Offset = (int) outputStream.Position, 59 | Size = Data.Length + 16 60 | }; 61 | return header; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/Glyph.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Xml.Serialization; 4 | 5 | namespace FfntTool.Ffnt 6 | { 7 | [XmlType("Glyph")] 8 | public class Glyph 9 | { 10 | public const int GlyphSize = 20; 11 | 12 | [XmlAttribute("Character")] 13 | public string CharacterString 14 | { 15 | get { return Character.ToString(); } 16 | set { Character = value.Length == 1 ? value[0] : ' '; } 17 | } 18 | 19 | [XmlIgnore] 20 | public char Character { get; set; } 21 | 22 | [XmlAttribute] 23 | public short XOffset { get; set; } 24 | 25 | [XmlAttribute] 26 | public short YOffset { get; set; } 27 | 28 | [XmlAttribute] 29 | public byte Width { get; set; } 30 | 31 | [XmlAttribute] 32 | public byte Height { get; set; } 33 | 34 | [XmlAttribute] 35 | public byte Layer { get; set; } 36 | 37 | [XmlAttribute] 38 | public byte HorizontalSpace { get; set; } 39 | 40 | [XmlAttribute] 41 | public byte HorizontalShift { get; set; } 42 | 43 | [XmlAttribute] 44 | public sbyte VerticalShift { get; set; } 45 | 46 | [XmlAttribute] 47 | public short Unknown1 { get; set; } 48 | 49 | [XmlAttribute] 50 | public int Unknown2 { get; set; } 51 | 52 | // TODO: Add a layer property 53 | 54 | 55 | public static Glyph ReadGlyph(Stream inputStream) 56 | { 57 | Glyph glyph = new Glyph(); 58 | glyph.Read(inputStream); 59 | return glyph; 60 | } 61 | 62 | public void Read(Stream inputStream) 63 | { 64 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Default, true); 65 | Character = (char) reader.ReadInt32(); 66 | XOffset = reader.ReadInt16(); 67 | YOffset = reader.ReadInt16(); 68 | Width = reader.ReadByte(); 69 | Height = reader.ReadByte(); 70 | Layer = reader.ReadByte(); 71 | HorizontalSpace = reader.ReadByte(); 72 | HorizontalShift = reader.ReadByte(); 73 | VerticalShift = reader.ReadSByte(); 74 | Unknown1 = reader.ReadInt16(); 75 | Unknown2 = reader.ReadInt32(); 76 | // TODO: Check which unknown properties are constant zero 77 | } 78 | 79 | public void Write(Stream outputStream) 80 | { 81 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 82 | writer.Write((int) Character); 83 | writer.Write(XOffset); 84 | writer.Write(YOffset); 85 | writer.Write(Width); 86 | writer.Write(Height); 87 | writer.Write(Layer); 88 | writer.Write(HorizontalSpace); 89 | writer.Write(HorizontalShift); 90 | writer.Write(VerticalShift); 91 | writer.Write(Unknown1); 92 | writer.Write(Unknown2); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /FfntTool/Ffnt/GlyphMap.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml.Serialization; 6 | 7 | namespace FfntTool.Ffnt 8 | { 9 | [XmlType("GlyphMap")] 10 | public class GlyphMap : FfntEntry 11 | { 12 | public const string GlyphSignature = "GLYP"; 13 | 14 | public GlyphMap() 15 | { 16 | Glyphs = new List(); 17 | } 18 | 19 | [XmlArray("Glyphs")] 20 | public List Glyphs { get; set; } 21 | 22 | public int Unknown1 { get; set; } 23 | public short Unknown2 { get; set; } 24 | public int Unknown3 { get; set; } 25 | 26 | public static GlyphMap ReadGlyphMap(Stream inputStream) 27 | { 28 | GlyphMap glyphMap = new GlyphMap(); 29 | glyphMap.Read(inputStream); 30 | return glyphMap; 31 | } 32 | 33 | public void Read(Stream inputStream) 34 | { 35 | BinaryReader reader = new BinaryReader(inputStream, Encoding.Default, true); 36 | Unknown1 = reader.ReadInt32(); 37 | Unknown2 = reader.ReadInt16(); 38 | short glyphCount = reader.ReadInt16(); 39 | int size = reader.ReadInt32(); 40 | Unknown3 = reader.ReadInt32(); 41 | for (int i = 0; i < glyphCount; i++) 42 | { 43 | Glyphs.Add(Glyph.ReadGlyph(inputStream)); 44 | } 45 | } 46 | 47 | public override void Write(Stream outputStream) 48 | { 49 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 50 | writer.Write(Unknown1); 51 | writer.Write(Unknown2); 52 | writer.Write((short) Glyphs.Count); 53 | writer.Write(GetAlignedSize(0)); 54 | writer.Write(Unknown3); 55 | foreach (var glyph in Glyphs.OrderBy(g => g.Character)) 56 | { 57 | glyph.Write(outputStream); 58 | } 59 | } 60 | 61 | public override FfntEntryHeader GetHeader(Stream outputStream) 62 | { 63 | FfntEntryHeader header = new FfntEntryHeader 64 | { 65 | FfntEntrySignature = GlyphSignature, 66 | Offset = (int) outputStream.Position, 67 | Size = GetAlignedSize(16) 68 | }; 69 | return header; 70 | } 71 | 72 | private int GetAlignedSize(int startSize) 73 | { 74 | int dataSize = Glyphs.Count*Glyph.GlyphSize + startSize; 75 | int alignmentRequired = dataSize%16; 76 | if (alignmentRequired > 0) 77 | dataSize += 16 - alignmentRequired; 78 | return dataSize; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /FfntTool/FfntTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4} 8 | Exe 9 | Properties 10 | FfntTool 11 | FfntTool 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 66 | -------------------------------------------------------------------------------- /FfntTool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Drawing.Imaging; 5 | using System.IO; 6 | using System.Linq; 7 | using System.Runtime.InteropServices; 8 | using System.Xml.Serialization; 9 | using FfntTool.Ffnt; 10 | 11 | namespace FfntTool 12 | { 13 | public static class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | if (args.Length < 1 || args.Length > 2) 18 | { 19 | return; 20 | } 21 | string path = args[0]; 22 | 23 | if (File.Exists(path) == false) 24 | { 25 | return; 26 | } 27 | 28 | string fileName = Path.GetFileNameWithoutExtension(path); 29 | string outputPath = args.Length == 2 30 | ? args[1] 31 | : Path.Combine(Path.GetDirectoryName(path), fileName); 32 | 33 | Directory.CreateDirectory(outputPath); 34 | 35 | if (path.EndsWith(".ffnt", StringComparison.InvariantCultureIgnoreCase)) 36 | { 37 | UnpackFfnt(path, fileName, outputPath); 38 | } 39 | else if (path.EndsWith(".xml", StringComparison.InvariantCultureIgnoreCase)) 40 | { 41 | PackFfnt(path, fileName, outputPath); 42 | } 43 | } 44 | 45 | private static void PackFfnt(string path, string fileName, string outputPath) 46 | { 47 | string outputFilePath = Path.Combine(outputPath, fileName + ".ffnt"); 48 | 49 | using (FileStream fontInputStream = new FileStream(path, FileMode.Open)) 50 | using (FileStream outputStream = new FileStream(outputFilePath, FileMode.Create)) 51 | { 52 | XmlSerializer serializer = new XmlSerializer(typeof (FfntFile), 53 | new[] {typeof (GlyphMap), typeof (FontData)}); 54 | 55 | 56 | FfntFile ffntFile = serializer.Deserialize(fontInputStream) as FfntFile; 57 | if (ffntFile != null) 58 | { 59 | ffntFile.Entries.OfType().Single().Data = ReadFontLayers(Path.GetDirectoryName(path), 60 | fileName); 61 | ffntFile.Write(outputStream); 62 | } 63 | } 64 | } 65 | 66 | private static byte[] ReadFontLayers(string directory, string fileName) 67 | { 68 | const int maxLayers = 8; 69 | List layers = new List(); 70 | 71 | for (int i = 0; i < maxLayers; i++) 72 | { 73 | byte[] layer = ReadFontLayer(directory, fileName, i); 74 | if (layer != null) 75 | { 76 | layers.Add(layer); 77 | } 78 | } 79 | 80 | if (layers.Count > 0) 81 | { 82 | byte[] result = new byte[layers.First().Length]; 83 | 84 | foreach (var layer in layers) 85 | { 86 | for (int i = 0; i < result.Length; i++) 87 | { 88 | result[i] = (byte) (result[i] | layer[i]); 89 | } 90 | } 91 | return result; 92 | } 93 | return null; 94 | } 95 | 96 | private static byte[] ReadFontLayer(string directory, string fileName, int layerIndex) 97 | { 98 | byte layerMask = (byte) (1 << layerIndex); 99 | string layerFilePath = Path.Combine(directory, string.Format("{0}_{1}" + ".png", fileName, layerIndex)); 100 | 101 | try 102 | { 103 | using (Image image = Image.FromFile(layerFilePath)) 104 | using (Bitmap bitmap = new Bitmap(image)) 105 | { 106 | byte[] result = new byte[bitmap.Width*bitmap.Height]; 107 | 108 | for (int y = 0; y < bitmap.Height; y++) 109 | { 110 | for (int x = 0; x < bitmap.Width; x++) 111 | { 112 | var pixel = bitmap.GetPixel(x, y); 113 | if (pixel.R == 255 && pixel.G == 255 && pixel.B == 255) 114 | { 115 | result[y*bitmap.Width + x] = layerMask; 116 | } 117 | } 118 | } 119 | return result; 120 | } 121 | } 122 | catch (FileNotFoundException) 123 | { 124 | return null; 125 | } 126 | } 127 | 128 | private static void UnpackFfnt(string path, string fileName, string outputPath) 129 | { 130 | using (FileStream inputStream = new FileStream(path, FileMode.Open)) 131 | { 132 | FfntFile ffntFile = FfntFile.ReadFfntFile(inputStream); 133 | 134 | if (ffntFile.Entries.Count != 2) 135 | return; 136 | 137 | GlyphMap glyphMap = ffntFile.Entries.ElementAt(0) as GlyphMap; 138 | FontData fontData = ffntFile.Entries.ElementAt(1) as FontData; 139 | 140 | if (glyphMap == null || fontData == null) 141 | return; 142 | 143 | SaveFont(ffntFile, fileName, outputPath); 144 | Size size = CalculateSize(fontData.Data.Length); 145 | SaveFontLayers(fontData.Data, size, fileName, outputPath); 146 | } 147 | } 148 | 149 | private static Size CalculateSize(int area) 150 | { 151 | int height; 152 | int width; 153 | if (Math.Sqrt(area)%1 == 0) // Squared (e.g. the latin font) 154 | { 155 | height = width = (int) Math.Sqrt(area); 156 | } 157 | else if (area/2%2 == 0 && Math.Sqrt(area/2)%1 == 0) // Rectangle with width = 2*height (e.g. the kanji font) 158 | { 159 | height = (int) Math.Sqrt(area/2); 160 | width = 2*height; 161 | } 162 | else 163 | { 164 | // TODO: Add width and height options to specify custom dimensions. 165 | throw new Exception("Unknown bitmap font dimensions."); 166 | } 167 | 168 | return new Size(width, height); 169 | } 170 | 171 | private static void SaveFont(FfntFile ffntFile, string fileName, string outputPath) 172 | { 173 | XmlSerializer serializer = new XmlSerializer(typeof (FfntFile), new[] {typeof (GlyphMap), typeof (FontData)}); 174 | 175 | string outputFilePath = Path.Combine(outputPath, fileName + ".xml"); 176 | using (var outputStream = new FileStream(outputFilePath, FileMode.Create)) 177 | { 178 | serializer.Serialize(outputStream, ffntFile); 179 | } 180 | } 181 | 182 | private static void SaveFontLayers(byte[] ffntData, Size size, string fileName, string outputPath) 183 | { 184 | const int maxLayers = 8; 185 | for (int i = 0; i < maxLayers; i++) 186 | { 187 | byte[] layer = GetLayer(ffntData, i); 188 | if (layer != null) 189 | { 190 | using (Bitmap bitmap = new Bitmap(size.Width, size.Height, PixelFormat.Format8bppIndexed)) 191 | { 192 | BitmapData bitmapData = bitmap.LockBits( 193 | new Rectangle(0, 0, bitmap.Width, bitmap.Height), 194 | ImageLockMode.WriteOnly, 195 | bitmap.PixelFormat); 196 | 197 | Marshal.Copy(layer, 0, bitmapData.Scan0, layer.Length); 198 | 199 | bitmap.UnlockBits(bitmapData); 200 | 201 | string outputFilePath = Path.Combine(outputPath, string.Format("{0}_{1}" + ".png", fileName, i)); 202 | bitmap.Save(outputFilePath, ImageFormat.Png); 203 | } 204 | } 205 | } 206 | } 207 | 208 | private static byte[] GetLayer(byte[] ffntData, int layerIndex) 209 | { 210 | int layerMask = 1 << layerIndex; 211 | byte[] layer = new byte[ffntData.Length]; 212 | bool emptyLayer = true; 213 | 214 | for (int i = 0; i < ffntData.Length; i++) 215 | { 216 | if ((ffntData[i] & layerMask) > 0) 217 | { 218 | layer[i] = 0xFF; 219 | emptyLayer = false; 220 | } 221 | } 222 | 223 | return emptyLayer ? null : layer; 224 | } 225 | } 226 | } 227 | -------------------------------------------------------------------------------- /FfntTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("FfntTool")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("FfntTool")] 9 | [assembly: AssemblyCopyright("Copyright © 2015 Atvaark")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("d9af2da5-85dd-45b2-8727-34be880d1558")] 14 | [assembly: AssemblyVersion("0.2.5.0")] 15 | [assembly: AssemblyFileVersion("0.2.5.0")] 16 | -------------------------------------------------------------------------------- /FoxEngine.TranslationTool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FfntTool", "FfntTool\FfntTool.csproj", "{12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubpTool", "SubpTool\SubpTool.csproj", "{64742682-F321-4073-A2D1-7516414FE0E7}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LangTool", "LangTool\LangTool.csproj", "{1C655336-E49C-4B48-8173-5CE433C909BE}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {12ABE4FC-F17F-4AAB-BFB1-11B5FFD56DC4}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {64742682-F321-4073-A2D1-7516414FE0E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {64742682-F321-4073-A2D1-7516414FE0E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {64742682-F321-4073-A2D1-7516414FE0E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {64742682-F321-4073-A2D1-7516414FE0E7}.Release|Any CPU.Build.0 = Release|Any CPU 26 | {1C655336-E49C-4B48-8173-5CE433C909BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {1C655336-E49C-4B48-8173-5CE433C909BE}.Debug|Any CPU.Build.0 = Debug|Any CPU 28 | {1C655336-E49C-4B48-8173-5CE433C909BE}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {1C655336-E49C-4B48-8173-5CE433C909BE}.Release|Any CPU.Build.0 = Release|Any CPU 30 | EndGlobalSection 31 | GlobalSection(SolutionProperties) = preSolution 32 | HideSolutionNode = FALSE 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Atvaark 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /LangTool/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /LangTool/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using System.Text; 4 | 5 | namespace LangTool 6 | { 7 | internal static class ExtensionMethods 8 | { 9 | internal static string ReadNullTerminatedString(this BinaryReader reader) 10 | { 11 | StringBuilder builder = new StringBuilder(); 12 | char nextCharacter; 13 | while ((nextCharacter = reader.ReadChar()) != 0x00) 14 | { 15 | builder.Append(nextCharacter); 16 | } 17 | return builder.ToString(); 18 | } 19 | 20 | internal static void WriteNullTerminatedString(this BinaryWriter writer, string text) 21 | { 22 | byte[] data = Encoding.UTF8.GetBytes(text + '\0'); 23 | writer.Write(data, 0, data.Length); 24 | } 25 | 26 | internal static void AlignWrite(this BinaryWriter writer, int alignment, byte data) 27 | { 28 | long alignmentRequired = writer.BaseStream.Position % alignment; 29 | byte[] alignmentBytes = Enumerable.Repeat(data, (int)(alignment - alignmentRequired)).ToArray(); 30 | writer.Write(alignmentBytes, 0, alignmentBytes.Length); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /LangTool/Lang/LangEntry.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Serialization; 2 | using LangTool.Utility; 3 | 4 | namespace LangTool.Lang 5 | { 6 | [XmlType("Entry")] 7 | public class LangEntry 8 | { 9 | [XmlAttribute] 10 | public uint Key { get; set; } 11 | 12 | [XmlAttribute] 13 | public string LangId { get; set; } 14 | 15 | [XmlIgnore] 16 | public int Offset { get; set; } 17 | 18 | [XmlAttribute] 19 | public short Color { get; set; } 20 | 21 | [XmlAttribute] 22 | public string Value { get; set; } 23 | 24 | public bool ShouldSerializeKey() 25 | { 26 | return string.IsNullOrEmpty(LangId); 27 | } 28 | 29 | public void UpdateKey() 30 | { 31 | if (!string.IsNullOrEmpty(LangId)) 32 | { 33 | Key = Fox.GetStrCode32(LangId); 34 | } 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LangTool/Lang/LangFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Xml.Serialization; 8 | using LangTool.Utility; 9 | 10 | namespace LangTool.Lang 11 | { 12 | [XmlRoot("LangFile")] 13 | public class LangFile 14 | { 15 | private const int LittleEndianConstant = 0x0000454C; // LE 16 | private const int BigEndianConstant = 0x00004542; // BE 17 | 18 | public LangFile() 19 | { 20 | Entries = new List(); 21 | } 22 | 23 | [XmlArray("Entries")] 24 | public List Entries { get; set; } 25 | 26 | [XmlAttribute("Endianess")] 27 | public Endianess Endianess { get; set; } 28 | 29 | public static LangFile ReadLangFile(Stream inputStream, Dictionary dictionary) 30 | { 31 | LangFile langFile = new LangFile(); 32 | langFile.Read(inputStream, dictionary); 33 | return langFile; 34 | } 35 | 36 | public void Read(Stream inputStream, Dictionary langIdDictionary) 37 | { 38 | BinaryReader headerReader = new BinaryReader(inputStream, Encoding.UTF8, true); 39 | BinaryReader reader; 40 | 41 | int magicNumber = headerReader.ReadInt32(); 42 | int version = headerReader.ReadInt32(); // GZ 2, TPP 3 43 | int endianess = headerReader.ReadInt32(); // LE, BE 44 | switch (endianess) 45 | { 46 | case LittleEndianConstant: // LE 47 | Endianess = Endianess.LittleEndian; 48 | reader = headerReader; 49 | break; 50 | case BigEndianConstant: // BE 51 | Endianess = Endianess.BigEndian; 52 | version = EndianessBitConverter.FlipEndianess(version); 53 | reader = new BigEndianBinaryReader(inputStream, Encoding.UTF8, true); 54 | break; 55 | default: 56 | throw new Exception(string.Format("Unknown endianess: {0:X}", endianess)); 57 | } 58 | 59 | int entryCount = reader.ReadInt32(); 60 | int valuesOffset = reader.ReadInt32(); 61 | int keysOffset = reader.ReadInt32(); 62 | 63 | inputStream.Position = valuesOffset; 64 | Dictionary offsetEntryDictionary = new Dictionary(); 65 | for (int i = 0; i < entryCount; i++) 66 | { 67 | int valuePosition = (int)inputStream.Position - valuesOffset; 68 | short colorId = headerReader.ReadInt16(); 69 | string value = reader.ReadNullTerminatedString(); 70 | offsetEntryDictionary.Add(valuePosition, new LangEntry 71 | { 72 | Color = colorId, 73 | Value = value 74 | }); 75 | } 76 | 77 | inputStream.Position = keysOffset; 78 | for (int i = 0; i < entryCount; i++) 79 | { 80 | uint langIdCode = reader.ReadUInt32(); 81 | int offset = reader.ReadInt32(); 82 | 83 | string langId; 84 | if (langIdDictionary.TryGetValue(langIdCode, out langId)) 85 | { 86 | offsetEntryDictionary[offset].LangId = langId; 87 | } 88 | 89 | offsetEntryDictionary[offset].Key = langIdCode; 90 | } 91 | 92 | Entries = offsetEntryDictionary.Values.ToList(); 93 | } 94 | 95 | public void Write(Stream outputStream) 96 | { 97 | BinaryWriter headerWriter = new BinaryWriter(outputStream, Encoding.UTF8, true); 98 | BinaryWriter writer; 99 | switch (Endianess) 100 | { 101 | case Endianess.LittleEndian: 102 | writer = headerWriter; 103 | break; 104 | case Endianess.BigEndian: 105 | writer = new BigEndianBinaryWriter(outputStream, Encoding.UTF8, true); 106 | break; 107 | default: 108 | throw new Exception("Unknown endianess " + Endianess); 109 | } 110 | 111 | long headerPosition = outputStream.Position; 112 | outputStream.Position += 24; 113 | 114 | int valuesPosition = (int)outputStream.Position; 115 | foreach (var entry in Entries) 116 | { 117 | entry.UpdateKey(); 118 | entry.Offset = (int)outputStream.Position - valuesPosition; 119 | headerWriter.Write(entry.Color); 120 | writer.WriteNullTerminatedString(entry.Value); 121 | } 122 | 123 | writer.AlignWrite(4, 0x00); 124 | 125 | int keysPosition = (int)outputStream.Position; 126 | foreach (var entry in Entries.OrderBy(e => e.Key).ThenByDescending(e => e.Offset)) 127 | { 128 | writer.Write(entry.Key); 129 | writer.Write(entry.Offset); 130 | } 131 | 132 | long endPosition = outputStream.Position; 133 | 134 | outputStream.Position = headerPosition; 135 | 136 | headerWriter.Write(0x474e414c); // LANG 137 | writer.Write(0x0000003); 138 | headerWriter.Write(Endianess == Endianess.LittleEndian ? LittleEndianConstant : BigEndianConstant); 139 | 140 | writer.Write(Entries.Count); 141 | writer.Write(valuesPosition); 142 | writer.Write(keysPosition); 143 | 144 | outputStream.Position = endPosition; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /LangTool/LangTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {1C655336-E49C-4B48-8173-5CE433C909BE} 8 | Exe 9 | Properties 10 | LangTool 11 | LangTool 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\CityHash.Net.Legacy.0.1.1\lib\net45\CityHash.dll 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | Always 65 | 66 | 67 | 68 | 75 | -------------------------------------------------------------------------------- /LangTool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Text; 6 | using System.Xml.Serialization; 7 | using LangTool.Lang; 8 | using LangTool.Utility; 9 | 10 | namespace LangTool 11 | { 12 | internal class Program 13 | { 14 | const string DefaultDictionaryPath = "lang_dictionary.txt"; 15 | 16 | private static void Main(string[] args) 17 | { 18 | if (args.Length != 1) 19 | { 20 | ShowUsageInfo(); 21 | return; 22 | } 23 | 24 | string path = args[0]; 25 | 26 | if (File.Exists(path) == false) 27 | { 28 | ShowUsageInfo(); 29 | return; 30 | } 31 | string extension = Path.GetExtension(path); 32 | if (String.Equals(extension, ".xml", StringComparison.OrdinalIgnoreCase)) 33 | { 34 | using (FileStream inputStream = new FileStream(path, FileMode.Open)) 35 | using (StreamReader xmlReader = new StreamReader(inputStream, Encoding.UTF8)) 36 | using (FileStream outputStream = new FileStream(path.Substring(0, path.Length - 4), FileMode.Create)) 37 | { 38 | XmlSerializer serializer = new XmlSerializer(typeof (LangFile)); 39 | LangFile file = serializer.Deserialize(xmlReader) as LangFile; 40 | if (file == null) 41 | { 42 | Console.WriteLine("XML was not not a valid LangFile"); 43 | return; 44 | } 45 | file.Write(outputStream); 46 | } 47 | } 48 | else if (String.Equals(extension, ".lng", StringComparison.OrdinalIgnoreCase) 49 | || String.Equals(extension, ".lng2", StringComparison.OrdinalIgnoreCase)) 50 | { 51 | var dictionary = GetDictionary(DefaultDictionaryPath); 52 | using (FileStream inputStream = new FileStream(path, FileMode.Open)) 53 | using (FileStream outputStream = new FileStream(path + ".xml", FileMode.Create)) 54 | using (StreamWriter xmlWriter = new StreamWriter(outputStream, Encoding.UTF8)) 55 | { 56 | LangFile file = LangFile.ReadLangFile(inputStream, dictionary); 57 | XmlSerializer serializer = new XmlSerializer(typeof (LangFile)); 58 | serializer.Serialize(xmlWriter, file); 59 | } 60 | } 61 | else 62 | { 63 | ShowUsageInfo(); 64 | } 65 | } 66 | 67 | private static Dictionary GetDictionary(string path) 68 | { 69 | var dictionary = new Dictionary(); 70 | try 71 | { 72 | var values = File.ReadAllLines(path); 73 | foreach (var value in values) 74 | { 75 | var code = Fox.GetStrCode32(value); 76 | DebugCheckCollision(dictionary, code, value); 77 | dictionary[code] = value; 78 | } 79 | } 80 | catch (Exception e) 81 | { 82 | Console.WriteLine("Unable to read the dictionary " + e); 83 | } 84 | 85 | return dictionary; 86 | } 87 | 88 | [Conditional("DEBUG")] 89 | private static void DebugCheckCollision(Dictionary dictionary, uint code, string newValue) 90 | { 91 | string originalValue; 92 | if (dictionary.TryGetValue(code, out originalValue)) 93 | { 94 | Debug.WriteLine("StrCode32 collision detected ({0}). Overwriting '{1}' with '{2}'", code, originalValue, newValue); 95 | } 96 | } 97 | 98 | private static void ShowUsageInfo() 99 | { 100 | Console.WriteLine("LangTool by Atvaark\n" + 101 | " A tool for converting between Fox Engine .lng/.lng2 files and xml.\n" + 102 | "Usage:\n" + 103 | " LangTool file_path.lng|file_path.lng2|file_path.xml\n" + 104 | "Examples:\n" + 105 | " LangTool gz_cassette.eng.lng - Converts the lng file to xml\n" + 106 | " LangTool gz_cassette.eng.lng.xml - Converts the xml file to lng"); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /LangTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("LangTool")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("LangTool")] 9 | [assembly: AssemblyCopyright("Copyright © 2015 Atvaark")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("b2857b4d-a9d2-4c25-84b8-c9f9a3612852")] 14 | [assembly: AssemblyVersion("0.2.5.0")] 15 | [assembly: AssemblyFileVersion("0.2.5.0")] 16 | -------------------------------------------------------------------------------- /LangTool/Utility/BigEndianBinaryReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace LangTool.Utility 6 | { 7 | public class BigEndianBinaryReader : BinaryReader 8 | { 9 | public BigEndianBinaryReader(Stream input) : base(input) 10 | { 11 | } 12 | 13 | public BigEndianBinaryReader(Stream input, Encoding encoding) : base(input, encoding) 14 | { 15 | } 16 | 17 | public BigEndianBinaryReader(Stream input, Encoding encoding, bool leaveOpen) : base(input, encoding, leaveOpen) 18 | { 19 | } 20 | 21 | private byte[] Reverse(byte[] b) 22 | { 23 | Array.Reverse(b); 24 | return b; 25 | } 26 | 27 | private byte[] ReadBytesRequired(int byteCount) 28 | { 29 | var result = ReadBytes(byteCount); 30 | 31 | if (result.Length != byteCount) 32 | throw new EndOfStreamException(string.Format("{0} bytes required from stream, but only {1} returned.", 33 | byteCount, result.Length)); 34 | 35 | return result; 36 | } 37 | 38 | public override decimal ReadDecimal() 39 | { 40 | byte[] buffer = Reverse(ReadBytesRequired(sizeof (decimal))); 41 | var i1 = BitConverter.ToInt32(buffer, 0); 42 | var i2 = BitConverter.ToInt32(buffer, 4); 43 | var i3 = BitConverter.ToInt32(buffer, 8); 44 | var i4 = BitConverter.ToInt32(buffer, 12); 45 | return new decimal(new[] {i1, i2, i3, i4}); 46 | } 47 | 48 | public override double ReadDouble() 49 | { 50 | return BitConverter.ToDouble(Reverse(ReadBytesRequired(sizeof (double))), 0); 51 | } 52 | 53 | public override short ReadInt16() 54 | { 55 | return BitConverter.ToInt16(Reverse(ReadBytesRequired(sizeof (Int16))), 0); 56 | } 57 | 58 | public override int ReadInt32() 59 | { 60 | return BitConverter.ToInt32(Reverse(ReadBytesRequired(sizeof (Int32))), 0); 61 | } 62 | 63 | public override long ReadInt64() 64 | { 65 | return BitConverter.ToInt64(Reverse(ReadBytesRequired(sizeof (Int64))), 0); 66 | } 67 | 68 | public override float ReadSingle() 69 | { 70 | return BitConverter.ToSingle(Reverse(ReadBytesRequired(sizeof (float))), 0); 71 | } 72 | 73 | public override ushort ReadUInt16() 74 | { 75 | return BitConverter.ToUInt16(Reverse(ReadBytesRequired(sizeof (UInt16))), 0); 76 | } 77 | 78 | public override uint ReadUInt32() 79 | { 80 | return BitConverter.ToUInt32(Reverse(ReadBytesRequired(sizeof (UInt32))), 0); 81 | } 82 | 83 | public override ulong ReadUInt64() 84 | { 85 | return BitConverter.ToUInt64(Reverse(ReadBytesRequired(sizeof (UInt64))), 0); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /LangTool/Utility/BigEndianBinaryWriter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace LangTool.Utility 6 | { 7 | public class BigEndianBinaryWriter : BinaryWriter 8 | { 9 | public BigEndianBinaryWriter(Stream output) : base(output) 10 | { 11 | 12 | } 13 | 14 | public BigEndianBinaryWriter(Stream output, Encoding encoding) 15 | : base(output, encoding) 16 | { 17 | 18 | } 19 | 20 | public BigEndianBinaryWriter(Stream output, Encoding encoding, bool leaveOpen) 21 | : base(output, encoding, leaveOpen) 22 | { 23 | 24 | } 25 | 26 | private byte[] Reverse(byte[] b) 27 | { 28 | Array.Reverse(b); 29 | return b; 30 | } 31 | 32 | public override void Write(float value) 33 | { 34 | Write(Reverse(BitConverter.GetBytes(value))); 35 | } 36 | 37 | public override void Write(ulong value) 38 | { 39 | Write(Reverse(BitConverter.GetBytes(value))); 40 | } 41 | 42 | public override void Write(long value) 43 | { 44 | Write(Reverse(BitConverter.GetBytes(value))); 45 | } 46 | 47 | public override void Write(uint value) 48 | { 49 | Write(Reverse(BitConverter.GetBytes(value))); 50 | } 51 | 52 | public override void Write(int value) 53 | { 54 | Write(Reverse(BitConverter.GetBytes(value))); 55 | } 56 | 57 | public override void Write(ushort value) 58 | { 59 | Write(Reverse(BitConverter.GetBytes(value))); 60 | } 61 | 62 | public override void Write(short value) 63 | { 64 | Write(Reverse(BitConverter.GetBytes(value))); 65 | } 66 | 67 | public override void Write(decimal value) 68 | { 69 | int[] bits = decimal.GetBits(value); 70 | byte[] bytes = new byte[4 * sizeof(int)]; 71 | Buffer.BlockCopy(bits, 0, bytes, 0, bytes.Length); 72 | Write(Reverse(bytes)); 73 | } 74 | 75 | public override void Write(double value) 76 | { 77 | Write(Reverse(BitConverter.GetBytes(value))); 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /LangTool/Utility/Endianess.cs: -------------------------------------------------------------------------------- 1 | namespace LangTool.Utility 2 | { 3 | public enum Endianess 4 | { 5 | LittleEndian, 6 | BigEndian 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /LangTool/Utility/EndianessBitConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LangTool.Utility 4 | { 5 | public static class EndianessBitConverter 6 | { 7 | private static byte[] Reverse(byte[] b) 8 | { 9 | Array.Reverse(b); 10 | return b; 11 | } 12 | 13 | public static int FlipEndianess(int value) 14 | { 15 | return BitConverter.ToInt32(Reverse(BitConverter.GetBytes(value)), 0); 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /LangTool/Utility/Fox.cs: -------------------------------------------------------------------------------- 1 | namespace LangTool.Utility 2 | { 3 | public static class Fox 4 | { 5 | public static uint GetStrCode32(string text) 6 | { 7 | const ulong seed0 = 0x9ae16a3b2f90404f; 8 | ulong seed1 = text.Length > 0 ? (uint)((text[0]) << 16) + (uint)text.Length : 0; 9 | return (uint)(CityHash.CityHash.CityHash64WithSeeds(text + "\0", seed0, seed1) & 0xFFFFFFFF); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LangTool/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FoxEngine.TranslationTool 2 | A bundle of tools related to text editing Fox Engine / MGSV Ground Zeroes files. 3 | 4 | This bundle contains the following tools: 5 | 6 | Name | Description 7 | --------- | ------------ 8 | FfntTool | Font unpacker/repacker 9 | LangTool | String table unpacker/repacker 10 | SubpTool | Subtitle unpacker/repacker 11 | 12 | 13 | ## Requirements 14 | ``` 15 | Microsoft .NET Framework 4.5 16 | ``` 17 | 18 | ## Remarks 19 | Repacking might lead to crashes or endless loading times with the current version. This is due to not every value being saved in the xml file. 20 | 21 | 22 | ## FfntTool 23 | A Fox Engine bitmap font (.ffnt) unpacker and repacker. 24 | 25 | ### Usage 26 | ``` 27 | FfntTool file_path [output_path] 28 | ``` 29 | 30 | ### Examples 31 | 32 | Unpacking a font file. This will create the file called *KanjiFont.ffnt.xml* and a folder called *KanjiFont*. The font bitmaps will be exported as png files to the *KanjiFont* folder. Each layer of the bitmap font is exported as a single black and white png image. 33 | ``` 34 | FfntTool KanjiFont.ffnt 35 | ``` 36 | 37 | Repacking a font file. This will create the file called *KanjiFont.ffnt*. The bitmap font layers will be read and merged. Only pixels with the color white will be included in the resulting font. 38 | ``` 39 | FfntTool KanjiFont.ffnt.xml 40 | ``` 41 | 42 | ## LangTool 43 | A Fox Engine localizable string table (.lng) unpacker and repacker. 44 | 45 | ### Usage 46 | ``` 47 | LangTool file_path [output_path] 48 | ``` 49 | 50 | ### Examples 51 | 52 | Unpacking an .lng file. This will create the file *gz_menu.lng#eng.xml*. 53 | ``` 54 | LangTool gz_menu.lng#eng 55 | ``` 56 | 57 | Repacking an .lng file. This will create the file *gz_menu.lng#eng* 58 | ``` 59 | LangTool gz_menu.lng#eng.xml 60 | ``` 61 | 62 | ## SubpTool 63 | A Fox Engine subtitle pack unpacker and repacker. 64 | 65 | ### Usage 66 | ``` 67 | SubpTool [options] file_path [output_path] 68 | ``` 69 | 70 | ### Options 71 | The language of the file may be specified to correctly decode certain subtitles. Not specifying a language option will default to the ISO-8859-1 (Latin 1) encoding. 72 | 73 | Option | Language | Encoding 74 | ------ | ---------- | -------- 75 | -eng | English | ISO-8859-1 76 | -fre | French | ISO-8859-1 77 | -ger | German | ISO-8859-1 78 | -ita | Italian | ISO-8859-1 79 | -spa | Spanish | ISO-8859-1 80 | -rus | Russian | ISO-8859-5 81 | -ara | Arabic | UTF-8 82 | -jpn | Japanese | UTF-8 83 | -por | Portuguese | UTF-8 84 | 85 | ### Examples 86 | 87 | Unpacking a subtitle package. 88 | ``` 89 | SubpTool common.subp 90 | ``` 91 | 92 | Unpacking an encoded subtitle package. 93 | ``` 94 | SubpTool -jpn JpnText\common.subp 95 | ``` 96 | 97 | Repacking a subtitle package. 98 | ``` 99 | SubpTool common.subp.xml 100 | ``` 101 | -------------------------------------------------------------------------------- /SubpTool/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SubpTool/ExtensionMethods.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace SubpTool 4 | { 5 | internal static class ExtensionMethods 6 | { 7 | internal static string ReadString(this BinaryReader binaryReader, int count) 8 | { 9 | return new string(binaryReader.ReadChars(count)); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SubpTool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml; 6 | using System.Xml.Schema; 7 | using System.Xml.Serialization; 8 | using SubpTool.Subp; 9 | 10 | namespace SubpTool 11 | { 12 | public static class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | if (args.Any() || args.Length > 2) 17 | { 18 | var encoding = GetEncodingFromArgument(args.Length == 2 ? args[0] : ""); 19 | string path = args.Length == 2 ? args[1] : args[0]; 20 | 21 | if (path.EndsWith(".subp")) 22 | { 23 | UnpackSubp(path, encoding); 24 | return; 25 | } 26 | if (path.EndsWith(".xml")) 27 | { 28 | PackSubp(path, encoding); 29 | return; 30 | } 31 | } 32 | Console.WriteLine("SubpTool by Atvaark\n" + 33 | "Description\n" + 34 | " Converts Fox Engine subtitle pack (.subp) files to xml. \n" + 35 | "Usage:\n" + 36 | " SubpTool.exe [options] filename.subp -Unpacks the subtitle pack file\n" + 37 | " SubpTool.exe [options] filename.xml -Packs the subtitle pack file \n" + 38 | "Options:\n" + 39 | " -ara, -eng, -fre, -ger, -ita, -jpn, -por, -rus and -spa"); 40 | } 41 | 42 | private static Encoding GetEncodingFromArgument(string encoding) 43 | { 44 | switch (encoding) 45 | { 46 | case "-rus": 47 | return Encoding.GetEncoding("ISO-8859-5"); 48 | case "-jpn": 49 | case "-ara": 50 | case "-por": 51 | return Encoding.UTF8; 52 | case "-fre": 53 | case "-ger": 54 | case "-spa": 55 | case "-ita": 56 | case "-eng": 57 | default: 58 | return Encoding.GetEncoding("ISO-8859-1"); 59 | } 60 | } 61 | 62 | private static void UnpackSubp(string path, Encoding encoding) 63 | { 64 | string fileDirectory = Path.GetDirectoryName(path); 65 | string fileName = Path.GetFileNameWithoutExtension(path); 66 | string outputFileName = fileName + ".xml"; 67 | string outputFilePath = Path.Combine(fileDirectory, outputFileName); 68 | 69 | 70 | using (FileStream inputStream = new FileStream(path, FileMode.Open)) 71 | using (XmlWriter outputWriter = XmlWriter.Create(outputFilePath, new XmlWriterSettings 72 | { 73 | NewLineHandling = NewLineHandling.Entitize, 74 | Indent = true 75 | })) 76 | { 77 | SubpFile subpFile = SubpFile.ReadSubpFile(inputStream, encoding); 78 | // TODO: Change XML Encoding 79 | XmlSerializer serializer = new XmlSerializer(typeof(SubpFile)); 80 | serializer.Serialize(outputWriter, subpFile); 81 | } 82 | } 83 | 84 | private static void PackSubp(string path, Encoding encoding) 85 | { 86 | string fileDirectory = Path.GetDirectoryName(path); 87 | string fileName = Path.GetFileNameWithoutExtension(path); 88 | string outputFileName = fileName + ".subp"; 89 | string outputFilePath = Path.Combine(fileDirectory, outputFileName); 90 | 91 | using (FileStream inputStream = new FileStream(path, FileMode.Open)) 92 | using (XmlReader xmlReader = XmlReader.Create(inputStream, CreateXmlReaderSettings())) 93 | using (FileStream outputStream = new FileStream(outputFilePath, FileMode.Create)) 94 | { 95 | XmlSerializer serializer = new XmlSerializer(typeof(SubpFile)); 96 | SubpFile subpFile = serializer.Deserialize(xmlReader) as SubpFile; 97 | subpFile?.Write(outputStream, encoding); 98 | } 99 | } 100 | 101 | private static XmlReaderSettings CreateXmlReaderSettings() 102 | { 103 | XmlSchemas schemas = new XmlSchemas(); 104 | XmlSchemaExporter exporter = new XmlSchemaExporter(schemas); 105 | XmlTypeMapping mapping = new XmlReflectionImporter().ImportTypeMapping(typeof(T)); 106 | exporter.ExportTypeMapping(mapping); 107 | XmlSchemaSet schemaSet = new XmlSchemaSet(); 108 | foreach (XmlSchema schema in schemas) 109 | { 110 | schemaSet.Add(schema); 111 | } 112 | 113 | XmlReaderSettings settings = new XmlReaderSettings(); 114 | settings.Schemas = schemaSet; 115 | settings.ValidationType = ValidationType.Schema; 116 | settings.ValidationEventHandler += HandleXmlReaderValidation; 117 | return settings; 118 | } 119 | 120 | private static void HandleXmlReaderValidation(object sender, ValidationEventArgs args) 121 | { 122 | if (args.Severity == XmlSeverityType.Warning) 123 | { 124 | Console.WriteLine($"{args.Severity} at line '{args.Exception?.LineNumber}' position '{args.Exception?.LinePosition}':\n{args.Message}"); 125 | } 126 | else 127 | { 128 | throw args.Exception; 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /SubpTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("SubpTool")] 5 | [assembly: AssemblyDescription("")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("SubpTool")] 9 | [assembly: AssemblyCopyright("Copyright © 2015 Atvaark")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | [assembly: ComVisible(false)] 13 | [assembly: Guid("2ab72241-1321-4bae-ba53-b12120bfc344")] 14 | [assembly: AssemblyVersion("0.2.6.0")] 15 | [assembly: AssemblyFileVersion("0.2.6.0")] 16 | -------------------------------------------------------------------------------- /SubpTool/Subp/SubpEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Xml.Serialization; 8 | 9 | namespace SubpTool.Subp 10 | { 11 | [XmlType("Entry")] 12 | public class SubpEntry 13 | { 14 | private const short MagicNumber = 0x4C01; 15 | 16 | public SubpEntry() 17 | { 18 | Lines = new List(); 19 | } 20 | 21 | [XmlAttribute("Id")] 22 | public uint SubtitleId { get; set; } 23 | 24 | [XmlAttribute("Priority")] 25 | public byte SubtitlePriority { get; set; } 26 | 27 | [XmlAttribute("Flags")] 28 | public short Flags { get; set; } 29 | 30 | [XmlAttribute("CharacterId")] 31 | public short CharacterId { get; set; } 32 | 33 | [XmlAttribute("AdditionalLength")] 34 | public short AdditionalLength { get; set; } 35 | 36 | [XmlArray("Lines")] 37 | public List Lines { get; set; } 38 | 39 | public static SubpEntry ReadSubpEntry(Stream input, Encoding encoding) 40 | { 41 | SubpEntry subpEntry = new SubpEntry(); 42 | subpEntry.Read(input, encoding); 43 | return subpEntry; 44 | } 45 | 46 | private void Read(Stream input, Encoding encoding) 47 | { 48 | BinaryReader reader = new BinaryReader(input, encoding, true); 49 | short magicNumber = reader.ReadInt16(); 50 | byte timingCount = reader.ReadByte(); 51 | SubtitlePriority = reader.ReadByte(); 52 | // TODO: Check if this is string length and (encoded) byte count. 53 | short stringLength1 = reader.ReadInt16(); 54 | short stringLength2 = reader.ReadInt16(); 55 | // TODO: Analyze what these values are used for 56 | AdditionalLength = Convert.ToInt16(stringLength2 - stringLength1); 57 | CharacterId = reader.ReadInt16(); 58 | Flags = reader.ReadInt16(); 59 | 60 | SubpTiming[] timings = new SubpTiming[timingCount]; 61 | for (int i = 0; i < timingCount; i++) 62 | { 63 | timings[i] = SubpTiming.ReadSubpTiming(input); 64 | } 65 | 66 | byte[] data = reader.ReadBytes(stringLength1); 67 | string subtitles = encoding.GetString(data).TrimEnd('\0'); 68 | 69 | // TODO: Check if the '$' literal can be escaped somehow 70 | // TODO: Check if Split('$').Count == lineCount 71 | string[] lines = subtitles.Split('$'); 72 | for (int i = 0; i < timingCount; i++) 73 | { 74 | Lines.Add(new SubpLine(lines.Length > i ? lines[i] : "", timings[i])); 75 | } 76 | 77 | if (lines.Length > timingCount) 78 | { 79 | // More lines than timings => Append without timing 80 | for (int i = timingCount; i < lines.Length; i++) 81 | { 82 | Lines.Add(new SubpLine(lines[i], null)); 83 | } 84 | } 85 | } 86 | 87 | public SubpIndex GetIndex(Stream outputStream) 88 | { 89 | return new SubpIndex 90 | { 91 | SubtitleId = SubtitleId, 92 | Offset = (uint) outputStream.Position 93 | }; 94 | } 95 | 96 | private string GetJoinedSubtitleLines() 97 | { 98 | return string.Join("$", Lines.Select(l => l.Text)); 99 | } 100 | 101 | public void Write(Stream outputStream, Encoding encoding) 102 | { 103 | BinaryWriter writer = new BinaryWriter(outputStream, encoding, true); 104 | writer.Write(MagicNumber); 105 | SubpTiming[] timings = Lines.Select(line => line.Timing).Where(timing => timing != null).ToArray(); 106 | writer.Write((byte)timings.Length); 107 | writer.Write(SubtitlePriority); 108 | 109 | string subtitles = GetJoinedSubtitleLines() + '\0'; 110 | byte[] encodedData = encoding.GetBytes(subtitles); 111 | 112 | writer.Write(Convert.ToInt16(encodedData.Length)); 113 | writer.Write(Convert.ToInt16(encodedData.Length + AdditionalLength)); 114 | writer.Write(CharacterId); 115 | writer.Write(Flags); 116 | 117 | foreach (var timing in timings) 118 | { 119 | timing.Write(outputStream); 120 | } 121 | 122 | writer.Write(encodedData); 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /SubpTool/Subp/SubpFile.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Text; 4 | using System.Xml.Serialization; 5 | 6 | namespace SubpTool.Subp 7 | { 8 | [XmlType("SubpFile")] 9 | public class SubpFile 10 | { 11 | private const short MagicNumber = 0x0113; 12 | 13 | public SubpFile() 14 | { 15 | Entries = new List(); 16 | } 17 | 18 | [XmlArray("Entries")] 19 | public List Entries { get; set; } 20 | 21 | public static SubpFile ReadSubpFile(Stream input, Encoding encoding) 22 | { 23 | SubpFile subpFile = new SubpFile(); 24 | subpFile.Read(input, encoding); 25 | return subpFile; 26 | } 27 | 28 | public void Read(Stream input, Encoding encoding) 29 | { 30 | BinaryReader reader = new BinaryReader(input, Encoding.Default, true); 31 | short magicNumber = reader.ReadInt16(); 32 | short entryCount = reader.ReadInt16(); 33 | 34 | List indices = new List(); 35 | for (int i = 0; i < entryCount; i++) 36 | { 37 | indices.Add(SubpIndex.ReadSubpIndex(input)); 38 | } 39 | 40 | foreach (var index in indices) 41 | { 42 | input.Position = index.Offset; 43 | var entry = SubpEntry.ReadSubpEntry(input, encoding); 44 | entry.SubtitleId = index.SubtitleId; 45 | Entries.Add(entry); 46 | } 47 | } 48 | 49 | public void Write(Stream outputStream, Encoding encoding) 50 | { 51 | BinaryWriter writer = new BinaryWriter(outputStream, encoding, true); 52 | writer.Write(MagicNumber); 53 | writer.Write((short) Entries.Count); 54 | long indicesPosition = outputStream.Position; 55 | outputStream.Position = outputStream.Position + SubpIndex.Size*Entries.Count; 56 | 57 | List indices = new List(); 58 | foreach (var entry in Entries) 59 | { 60 | indices.Add(entry.GetIndex(outputStream)); 61 | entry.Write(outputStream, encoding); 62 | } 63 | long endPosition = outputStream.Position; 64 | outputStream.Position = indicesPosition; 65 | foreach (var index in indices) 66 | { 67 | index.Write(outputStream); 68 | } 69 | outputStream.Position = endPosition; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SubpTool/Subp/SubpIndex.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace SubpTool.Subp 5 | { 6 | public class SubpIndex 7 | { 8 | public const int Size = 8; 9 | public uint SubtitleId { get; set; } 10 | public uint Offset { get; set; } 11 | 12 | public static SubpIndex ReadSubpIndex(Stream input) 13 | { 14 | SubpIndex subpIndex = new SubpIndex(); 15 | subpIndex.Read(input); 16 | return subpIndex; 17 | } 18 | 19 | private void Read(Stream input) 20 | { 21 | BinaryReader reader = new BinaryReader(input, Encoding.Default, true); 22 | SubtitleId = reader.ReadUInt32(); 23 | Offset = reader.ReadUInt32(); 24 | } 25 | 26 | public void Write(Stream outputStream) 27 | { 28 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 29 | writer.Write(SubtitleId); 30 | writer.Write(Offset); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /SubpTool/Subp/SubpLine.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Serialization; 2 | 3 | namespace SubpTool.Subp 4 | { 5 | [XmlType("Line")] 6 | public class SubpLine 7 | { 8 | public SubpLine() 9 | { 10 | } 11 | 12 | public SubpLine(string text, SubpTiming timing) 13 | { 14 | Text = text; 15 | Timing = timing; 16 | } 17 | 18 | [XmlAttribute("Text")] 19 | public string Text { get; set; } 20 | 21 | [XmlElement("Timing", IsNullable = true)] 22 | public SubpTiming Timing { get; set; } 23 | 24 | public bool ShouldSerializeTiming() 25 | { 26 | return Timing != null; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SubpTool/Subp/SubpTiming.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | using System.Xml.Serialization; 4 | 5 | namespace SubpTool.Subp 6 | { 7 | public class SubpTiming 8 | { 9 | public static SubpTiming Null = new SubpTiming(); 10 | 11 | [XmlAttribute("Start")] 12 | public ushort Start { get; set; } 13 | 14 | [XmlAttribute("End")] 15 | public ushort End { get; set; } 16 | 17 | public static SubpTiming ReadSubpTiming(Stream input) 18 | { 19 | SubpTiming subpTiming = new SubpTiming(); 20 | subpTiming.Read(input); 21 | return subpTiming; 22 | } 23 | 24 | private void Read(Stream input) 25 | { 26 | BinaryReader reader = new BinaryReader(input, Encoding.Default, true); 27 | Start = reader.ReadUInt16(); 28 | End = reader.ReadUInt16(); 29 | } 30 | 31 | public void Write(Stream outputStream) 32 | { 33 | BinaryWriter writer = new BinaryWriter(outputStream, Encoding.Default, true); 34 | writer.Write(Start); 35 | writer.Write(End); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SubpTool/SubpTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {64742682-F321-4073-A2D1-7516414FE0E7} 8 | Exe 9 | Properties 10 | SubpTool 11 | SubpTool 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | --------------------------------------------------------------------------------