Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions WoomLink/Converter/NamesTextParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using System.IO;
using WoomLink.sead;

namespace WoomLink.Converter;

/// <summary>
/// Reads a plain text file containing one name per line and builds a
/// CRC32 hash-to-name lookup table for resolving xlink user hashes.
///
/// Example names.txt:
/// Player
/// Enemy_Bokoblin
/// Npc_Zelda
/// </summary>
public static class NamesTextParser
{
public static Dictionary<uint, string> Parse(string path)
{
var map = new Dictionary<uint, string>();

foreach (var rawLine in File.ReadLines(path))
{
var name = rawLine.Trim();

// Skip empty lines and comment lines (starting with #)
if (name.Length == 0 || name.StartsWith('#'))
continue;

uint hash = HashCrc32.CalcStringHash(name);
map.TryAdd(hash, name);
}

return map;
}

/// <summary>
/// Merges <paramref name="source"/> into <paramref name="target"/>.
/// Entries already present in <paramref name="target"/> are kept as-is
/// (actordb.yaml takes precedence when both files are supplied).
/// </summary>
public static void MergeInto(Dictionary<uint, string> target, Dictionary<uint, string> source)
{
foreach (var (hash, name) in source)
target.TryAdd(hash, name);
}
}
28 changes: 26 additions & 2 deletions WoomLink/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,10 @@ static void PrintUsage()
Console.Error.WriteLine("WoomLink - XLink binary converter");
Console.Error.WriteLine();
Console.Error.WriteLine("Convert binary to text:");
Console.Error.WriteLine(" WoomLink convert <input.belnk|bslnk> [--output <file.txt>] [--actors <ActorDb.yaml>]");
Console.Error.WriteLine(" WoomLink convert <input.belnk|bslnk> [--output <file.txt>] [--actors <ActorDb.yaml>] [--names <names.txt>]");
Console.Error.WriteLine();
Console.Error.WriteLine(" --actors ActorDb.yaml resolve hashes from a BOTW/TOTK actor YAML database");
Console.Error.WriteLine(" --names names.txt resolve hashes from a plain text file (one name per line)");
Console.Error.WriteLine();
Console.Error.WriteLine("Rebuild text to binary:");
Console.Error.WriteLine(" WoomLink rebuild <input.txt> [--output <file.belnk|bslnk>]");
Expand All @@ -60,32 +63,53 @@ static void RunConvert(string[] args)
{
if (args.Length < 2)
{
Console.Error.WriteLine("Usage: WoomLink convert <input> [--output <file>] [--actors <ActorDb.yaml>]");
Console.Error.WriteLine("Usage: WoomLink convert <input> [--output <file>] [--actors <ActorDb.yaml>] [--names <names.txt>]");
return;
}

string input = args[1];
string? output = null;
string? actorsPath = null;
string? namesPath = null;
for (int i = 2; i < args.Length; i++)
{
if (args[i] == "--output" && i + 1 < args.Length)
output = args[++i];
else if (args[i] == "--actors" && i + 1 < args.Length)
actorsPath = args[++i];
else if (args[i] == "--names" && i + 1 < args.Length)
namesPath = args[++i];
}

var data = File.ReadAllBytes(input);
var reader = new Converter.XLinkBinaryReader();
var model = reader.Read(data);

Dictionary<uint, string>? actorNames = null;

if (actorsPath != null)
{
actorNames = Converter.ActorYamlParser.Parse(actorsPath);
Console.Error.WriteLine($"Loaded {actorNames.Count} actor names from {actorsPath}");
}

if (namesPath != null)
{
var namesFromTxt = Converter.NamesTextParser.Parse(namesPath);
Console.Error.WriteLine($"Loaded {namesFromTxt.Count} names from {namesPath}");

if (actorNames == null)
{
actorNames = namesFromTxt;
}
else
{
// Merge: actordb.yaml takes precedence for collisions
Converter.NamesTextParser.MergeInto(actorNames, namesFromTxt);
Console.Error.WriteLine($"Combined lookup table has {actorNames.Count} entries");
}
}

System.IO.TextWriter writer;
if (output != null)
{
Expand Down