Created
February 2, 2023 18:12
-
-
Save LinkOFF7/adf3c2dcb834482d83dd23d172b26b15 to your computer and use it in GitHub Desktop.
Need for Speed Heat/Unbound Cyrillic Encoder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.IO; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using System.Threading.Tasks; | |
using tomodachi; | |
namespace FrostbiteResearch | |
{ | |
internal class NFSLocale | |
{ | |
//all offsets without 8 bytes | |
public uint Signature = 0x039000; | |
public int FileSize { get; set; } | |
public int EntriesNum { get; set; } | |
public int TableOffset { get; set; } | |
public int DataOffset { get; set; } | |
public string Type { get; set; } | |
List<Entry> Entries { get; set; } | |
public void ExtractText(string outputFile) | |
{ | |
List<string> text = new List<string>(); | |
foreach (Entry entry in Entries) | |
{ | |
text.Add($"{entry.String.Replace("\n", " ")}"); | |
} | |
File.WriteAllLines(outputFile, text.ToArray()); | |
} | |
public void WriteFromText(string textFile, string outputChunk, string idsFile) | |
{ | |
string[] text = File.ReadAllLines(textFile); | |
string[] ids_ = File.ReadAllLines(idsFile); | |
if(text.Length != ids_.Length) | |
{ | |
Console.WriteLine($"Несоответствие кол-ва строк в файле {Path.GetFileName(textFile)} к ID в файле {Path.GetFileName(idsFile)}"); | |
Console.WriteLine($"{text.Length}/{ids_.Length}"); | |
return; | |
} | |
uint[] ids = new uint[text.Length]; | |
uint[] offsets = new uint[text.Length]; | |
for(int i = 0; i < ids_.Length; i++) | |
{ | |
ids[i] = UInt32.Parse(ids_[i], System.Globalization.NumberStyles.HexNumber); | |
} | |
using(BinaryWriter writer = new BinaryWriter(File.Create(outputChunk))) | |
{ | |
writer.Write(Signature); | |
writer.Write(0); //skip filesize | |
writer.Write(text.Length); | |
writer.Write(140); //table offset | |
int textStart = (text.Length * 8) + 0x8C;//calculate text offset | |
writer.Write(textStart); | |
writer.Write(Encoding.ASCII.GetBytes("Default")); | |
writer.BaseStream.Position = textStart + 8; | |
for(int i = 0; i < text.Length; i++) | |
{ | |
offsets[i] = (uint)((writer.BaseStream.Position - textStart) - 8); | |
writer.Write(NFSEncoder(text[i].Replace("\"\"", "\""))); | |
writer.Write(new byte()); //null term | |
} | |
writer.BaseStream.Position = 0x94; | |
for (int i = 0; i < text.Length; i++) | |
{ | |
writer.Write(ids[i]); | |
writer.Write(offsets[i]); | |
} | |
writer.BaseStream.Position = 4; | |
writer.Write((uint)(writer.BaseStream.Length - 8)); | |
} | |
} | |
public void Read(string file) | |
{ | |
using(BinaryReader reader = new BinaryReader(File.OpenRead(file))) | |
{ | |
if (reader.ReadUInt32() != Signature) | |
throw new Exception("Unknown file"); | |
FileSize = reader.ReadInt32(); | |
EntriesNum = reader.ReadInt32(); | |
TableOffset = reader.ReadInt32(); | |
DataOffset = reader.ReadInt32(); | |
Type = Utils.ReadString(reader, Encoding.ASCII); | |
Entries = new List<Entry>(); | |
reader.BaseStream.Position = TableOffset + 8; | |
for(int i = 0; i < EntriesNum; i++) | |
{ | |
Entries.Add(new Entry() | |
{ | |
Id = reader.ReadUInt32(), | |
Offset = reader.ReadInt32() + DataOffset + 8 | |
}); | |
} | |
for(int i = 0; i < EntriesNum; i++) | |
{ | |
Entry entry = Entries[i]; | |
reader.BaseStream.Position = entry.Offset; | |
entry.String = NFSDecoder(Utils.ReadNullTerminatedArray(reader)); | |
Entries[i] = entry; | |
} | |
File.WriteAllLines(file + ".ids", Entries.Select(i => i.Id.ToString("X8"))); | |
} | |
} | |
private byte[] NFSEncoder(string text) | |
{ | |
List<byte> result = new List<byte>(); | |
for(int i = 0; i < text.Length; i++) | |
{ | |
switch (text[i]) | |
{ | |
case 'А': result.Add(0x8D); break; | |
case 'Б': result.Add(0xB9); break; | |
case 'В': result.Add(0xA4); break; | |
case 'Г': result.Add(0xAD); break; | |
case 'Д': result.Add(0xA2); break; | |
case 'Е': result.Add(0x97); break; | |
case 'Ё': result.Add(0xCB); break; | |
case 'Ж': result.Add(0xC3); break; | |
case 'З': result.Add(0xB2); break; | |
case 'И': result.Add(0x96); break; | |
case 'Й': result.Add(0xB6); break; | |
case 'К': result.Add(0x9A); break; | |
case 'Л': result.Add(0xA7); break; | |
case 'М': result.Add(0xAC); break; | |
case 'Н': result.Add(0x91); break; | |
case 'О': result.Add(0x92); break; | |
case 'П': result.Add(0xA3); break; | |
case 'Р': result.Add(0x99); break; | |
case 'С': result.Add(0x9C); break; | |
case 'Т': result.Add(0x9E); break; | |
case 'У': result.Add(0xA8); break; | |
case 'Ф': result.Add(0xC5); break; | |
case 'Х': result.Add(0xC4); break; | |
case 'Ц': result.Add(0xC7); break; | |
case 'Ч': result.Add(0xBC); break; | |
case 'Ш': result.Add(0xC2); break; | |
case 'Щ': result.Add(0xCC); break; | |
case 'Ъ': result.Add(0xD2); break; | |
case 'Ы': result.Add(0xB5); break; | |
case 'Ь': result.Add(0xBA); break; | |
case 'Э': result.Add(0xB8); break; | |
case 'Ю': result.Add(0xCA); break; | |
case 'Я': result.Add(0xB3); break; | |
case 'а': result.Add(0x83); break; | |
case 'б': result.Add(0x98); break; | |
case 'в': result.Add(0x89); break; | |
case 'г': result.Add(0x9B); break; | |
case 'д': result.Add(0x8B); break; | |
case 'е': result.Add(0x82); break; | |
case 'ё': result.Add(0xB1); break; | |
case 'ж': result.Add(0xA6); break; | |
case 'з': result.Add(0x9D); break; | |
case 'и': result.Add(0x85); break; | |
case 'й': result.Add(0xA1); break; | |
case 'к': result.Add(0x8C); break; | |
case 'л': result.Add(0x8A); break; | |
case 'м': result.Add(0x8E); break; | |
case 'н': result.Add(0x86); break; | |
case 'о': result.Add(0x81); break; | |
case 'п': result.Add(0x90); break; | |
case 'р': result.Add(0x88); break; | |
case 'с': result.Add(0x87); break; | |
case 'т': result.Add(0x84); break; | |
case 'у': result.Add(0x8F); break; | |
case 'ф': result.Add(0xBD); break; | |
case 'х': result.Add(0xAA); break; | |
case 'ц': result.Add(0xC0); break; | |
case 'ч': result.Add(0x9F); break; | |
case 'ш': result.Add(0xA5); break; | |
case 'щ': result.Add(0xBF); break; | |
case 'ъ': result.Add(0xCF); break; | |
case 'ы': result.Add(0x93); break; | |
case 'ь': result.Add(0x94); break; | |
case 'э': result.Add(0xBE); break; | |
case 'ю': result.Add(0xAF); break; | |
case 'я': result.Add(0x95); break; | |
case '—': result.Add(0xC6); break; | |
case '…': result.Add(0xD1); break; | |
case '™': result.Add(0xD0); break; | |
case '“': result.Add(0xCD); break; | |
case '’': result.Add(0xD4); break; | |
case '”': result.Add(0xCE); break; | |
case '–': result.Add(0xD3); break; | |
case '': result.Add(0xD5); break; | |
case '№': result.Add(0xD6); break; | |
case '●': result.Add(0xD7); break; | |
default: result.Add((byte)text[i]); break; | |
} | |
} | |
return result.ToArray(); | |
} | |
private string NFSDecoder(byte[] data) | |
{ | |
string result = ""; | |
for(int i = 0; i < data.Length; i++) | |
{ | |
switch (data[i]) | |
{ | |
case 0x8D: result += "А"; break; | |
case 0xB9: result += "Б"; break; | |
case 0xA4: result += "В"; break; | |
case 0xAD: result += "Г"; break; | |
case 0xA2: result += "Д"; break; | |
case 0x97: result += "Е"; break; | |
case 0xCB: result += "Ё"; break; | |
case 0xC3: result += "Ж"; break; | |
case 0xB2: result += "З"; break; | |
case 0x96: result += "И"; break; | |
case 0xB6: result += "Й"; break; | |
case 0x9A: result += "К"; break; | |
case 0xA7: result += "Л"; break; | |
case 0xAC: result += "М"; break; | |
case 0x91: result += "Н"; break; | |
case 0x92: result += "О"; break; | |
case 0xA3: result += "П"; break; | |
case 0x99: result += "Р"; break; | |
case 0x9C: result += "С"; break; | |
case 0x9E: result += "Т"; break; | |
case 0xA8: result += "У"; break; | |
case 0xC5: result += "Ф"; break; | |
case 0xC4: result += "Х"; break; | |
case 0xC7: result += "Ц"; break; | |
case 0xBC: result += "Ч"; break; | |
case 0xC2: result += "Ш"; break; | |
case 0xCC: result += "Щ"; break; | |
case 0xD2: result += "Ъ"; break; | |
case 0xB5: result += "Ы"; break; | |
case 0xBA: result += "Ь"; break; | |
case 0xB8: result += "Э"; break; | |
case 0xCA: result += "Ю"; break; | |
case 0xB3: result += "Я"; break; | |
case 0x83: result += "а"; break; | |
case 0x98: result += "б"; break; | |
case 0x89: result += "в"; break; | |
case 0x9B: result += "г"; break; | |
case 0x8B: result += "д"; break; | |
case 0x82: result += "е"; break; | |
case 0xB1: result += "ё"; break; | |
case 0xA6: result += "ж"; break; | |
case 0x9D: result += "з"; break; | |
case 0x85: result += "и"; break; | |
case 0xA1: result += "й"; break; | |
case 0x8C: result += "к"; break; | |
case 0x8A: result += "л"; break; | |
case 0x8E: result += "м"; break; | |
case 0x86: result += "н"; break; | |
case 0x81: result += "о"; break; | |
case 0x90: result += "п"; break; | |
case 0x88: result += "р"; break; | |
case 0x87: result += "с"; break; | |
case 0x84: result += "т"; break; | |
case 0x8F: result += "у"; break; | |
case 0xBD: result += "ф"; break; | |
case 0xAA: result += "х"; break; | |
case 0xC0: result += "ц"; break; | |
case 0x9F: result += "ч"; break; | |
case 0xA5: result += "ш"; break; | |
case 0xBF: result += "щ"; break; | |
case 0xCF: result += "ъ"; break; | |
case 0x93: result += "ы"; break; | |
case 0x94: result += "ь"; break; | |
case 0xBE: result += "э"; break; | |
case 0xAF: result += "ю"; break; | |
case 0x95: result += "я"; break; | |
case 0xC6: result += "—"; break; | |
case 0xD1: result += "…"; break; | |
case 0xD0: result += "™"; break; | |
case 0xCD: result += "“"; break; | |
case 0xD4: result += "’"; break; | |
case 0xCE: result += "”"; break; | |
case 0xD3: result += "–"; break; | |
case 0xD5: result += ""; break; | |
case 0xD6: result += "№"; break; | |
case 0xD7: result += "●"; break; | |
default: result += (char)data[i]; break; | |
} | |
} | |
return result; | |
} | |
} | |
internal class Entry | |
{ | |
public uint Id { get; set; } | |
public int Offset { get; set; } | |
public byte[] StringArray { get; set; } | |
public string String { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment