• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Extracting Object Data for Programmatic Object Creation

Level 2
Joined
Jan 5, 2023
Messages
8
I am recreating a map using Dencer/DengSir's and Luangzhi's fantastic VS Code extension warcraft-vscode (it's posted here on Hive Workshop and also on GitHub) which features programmatic object creation and editing.

I have a .w3u file that I can open in the regular World Editor and view the unit data no problem. What I'm looking for is a tool or a way to extract the unit data into some formatted data structure, like a JSON object for example. It doesn't have to be JSON specifically, just any data format that I can write a script to parse the data into code that I can use for programmatic object creation.

The object creation syntax looks like this:
local h101 = UnitDefinition:new('H101', 'Hpal')
h101:setName('Hello objediting')
local i101 = ItemDefinition:new('I101', 'ratf')
i101:setInterfaceIcon('ReplaceableTextures\\CommandButtons\\BTNDarkSummoning.blp')

For custom object creation, the first parameter is the unit ID for the newly created custom unit, and the second is the super ID of the base unit used to initialize the custom unit's data fields. And then you can reassign all of your custom unit's data fields to whatever you want them to be.

You can probably see where I'm going with this. I want a formatted data list of a .w3u file's unit data that I can parse into this object editing syntax to be able to further customize unit creation in my new map.
 
Level 2
Joined
Jan 5, 2023
Messages
8
OK I used that git repo I posted above, War3Net, and I was able to extract all of the unit data. And it was a pain in the ass haha. I will post how I did it a bit later. But I have some question about the data I am getting, here is a snippet of the output:
{
"OldId": 1818325064,
"NewId": 1110454344,
"Modifications": [
{
"Id": 1835101813,
"Type": 3,
"Value": "Archer",
"SanityCheck": 0
},
{
"Id": 1650550901,
"Type": 3,
"Value": "A00E,A00H,A00G,A00F",
"SanityCheck": 0
},
{
"Id": 1869770869,
"Type": 3,
"Value": "Althena",
"SanityCheck": 0
},
{
"Id": 1953980789,
"Type": 3,
"Value": "",
"SanityCheck": 0
},
{
"Id": 1885959285,
"Type": 3,
"Value": "",
"SanityCheck": 0
},
{
"Id": 1651864693,
"Type": 3,
"Value": "",
"SanityCheck": 0
},
{
"Id": 1919972469,
"Type": 3,
"Value": "",
"SanityCheck": 0
},
{
"Id": 1818520949,
"Type": 3,
"Value": "units\\nightelf\\Archer\\Archer.mdl",
"SanityCheck": 0
},
{
"Id": 1868786037,
"Type": 3,
"Value": "ReplaceableTextures\\CommandButtons\\BTNArcher.blp",
"SanityCheck": 0
},
{
"Id": 1684960117,
"Type": 3,
"Value": "Archer",
"SanityCheck": 0
},
{
"Id": 1634889845,
"Type": 3,
"Value": "AGI",
"SanityCheck": 0
},
{
"Id": 1915838837,
"Type": 0,
"Value": 600,
"SanityCheck": 0
},
{
"Id": 829645685,
"Type": 3,
"Value": "",
"SanityCheck": 0
},
{
"Id": 1836212597,
"Type": 3,
"Value": "Flesh",
"SanityCheck": 0
},
{
"Id": 1769173877,
"Type": 3,
"Value": "ReplaceableTextures\\CommandButtons\\BTNArcher.blp",
"SanityCheck": 0
},


Looks pretty good :D we can see that the appropriate data is coming through. But now I have to parse it! The Ids are consistent across units. Meaning that the last ID, 1769173877, will always have a value corresponding to that unit's command button. So we can definitely work with this. But I have a couple questions:

1) Is there a map of which unit properties these Ids correspond to? Sure, we can infer from looking at the values, but it would be great if there was a map that's already out there that we can use to help map each Id to it's unit property.
2) What are the Type and Sanity check values?

Anyone have any insight on this?
 
Level 2
Joined
Jan 5, 2023
Messages
8
OK the Ids are the outputs of FourCC. And they can be converted back to strings using luabitop.

First install it using
luarocks install luabitop

Then use this function to convert
local bit = require("bit")

function GetStringFromId(id)
local b1 = bit.band(bit.rshift(id, 24), 0xFF)
local b2 = bit.band(bit.rshift(id, 16), 0xFF)
local b3 = bit.band(bit.rshift(id, 8), 0xFF)
local b4 = bit.band(id, 0xFF)
return string.char(b1, b2, b3, b4)
end

Nice. Still don't know what Type or SanityCheck are though.
 
Level 2
Joined
Jan 5, 2023
Messages
8
I have to clean up the code but here is what I used to serialize the unit data from the map

using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.Json;
using War3Net.Build;
using War3Net.Build.Object;
namespace YourNamespace
{
public static class UnitObjectDataExtensions
{
public static void PublicWriteTo(this UnitObjectData data, Utf8JsonWriter writer, JsonSerializerOptions options)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data), "The data instance is null.");
}
// Obtain a reference to the 'WriteTo' method with specific parameter types
var methodInfo = typeof(UnitObjectData).GetMethod("WriteTo", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Utf8JsonWriter), typeof(JsonSerializerOptions) }, null);
// Check if the method was found
if (methodInfo == null)
{
throw new InvalidOperationException("The 'WriteTo' method was not found.");
}
// Invoke the method using reflection on the 'data' instance
methodInfo.Invoke(data, new object[] { writer, options });
}
}
class Program
{
static void Main()
{
Map map = Map.Open(@"C:\Users\Kyle\Desktop\teve-deprotected.w3x");
UnitObjectData data = map.UnitObjectData;
// Serialize the data to JSON
var options = new JsonSerializerOptions();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
string outputPath = "output.json";
data.PublicWriteTo(writer, options);
writer.Flush();
stream.Position = 0;
File.WriteAllBytes(outputPath, stream.ToArray());
Console.WriteLine($"Data written to {outputPath}");
}
}
}
Using .NET 7.0
Initialize a new .NET project
Cd to it
Add this package
dotnet add package War3Net.Build.Core
Copy the above code to Program.cs
Run it
dotnet run
And the output will be in the root project directory in output.json
I plan to expand this to serialize the other object data as well.
 
Top