BW2 General Modding Discussion + Help

Matt

Administrator
Staff member
Joined
Aug 28, 2005
Messages
201
Any questions or discussion about Black & White 2 or BoTG can be asked here. I will do my best to answer your questions.

Check out my bw2-modding repository. You can find information about file formats used in Black & White 2, scripting language documentation and more on the LHVM (Lionhead Virtual Machine).
 
Thanks for the topic! :)

I was wondering if it was possible to activate the BOTG spells for the main game as well.. (so lava etc.)
I saw that there are entries for these miracles in the balance files for BW2. Does that mean that the game already has them implemented but maybe they are deactivated?

Is it somehow possible to change all greek banners to different ones?
I want to use the Japanese plattoon flag. however, the game somehow only accepts m_bannergeneric and then uses this file for all the other flages.
For example, I put the Japanese melee flag into the textures ordner and rename it to bannergeneric, all my armies (also the archers) have this flag. The migrants coming to my town also do.

Is it also possible to activate the buildings of BOTG for the main game to?
And can I somehow change that my army flag says: Japanese instead of Greek when I put my hand above the flag and the information box appears?

Sorry for all the questions  :)
 
sunny said:
I was wondering if it was possible to activate the BOTG spells for the main game as well.. (so lava etc.)
I saw that there are entries for these miracles in the balance files for BW2. Does that mean that the game already has them implemented but maybe they are deactivated?

I've tried manually activating the spells through binary modifications and they're just broken and crash the game, no real way to fix them either :(

sunny said:
Is it also possible to activate the buildings of BOTG for the main game to?

The buildings for BOTG are hard coded into the executable itself, no way to back port them sorry.

I can't remember if it was possible or not to make maps for BoTG? Was it something like the script compiler was never released? Or does the landscape editor not work for BoTG either?


sunny said:
Is it somehow possible to change all greek banners to different ones?
I want to use the Japanese plattoon flag. however, the game somehow only accepts m_bannergeneric and then uses this file for all the other flages.
For example, I put the Japanese melee flag into the textures ordner and rename it to bannergeneric, all my armies (also the archers) have this flag. The migrants coming to my town also do.

Surely you just rename all the greek flags to japanese and all the japanese to greek?

sunny said:
And can I somehow change that my army flag says: Japanese instead of Greek when I put my hand above the flag and the information box appears?

A lot of the text ingame is stored in Data/Text/bw2text.bin - you might be able to find what you're looking for in there, the ingame text is encoded as UTF-16, you will need a hex editor to edit it - as I don't think there was ever a tool released to edit these files.

sunny said:
Sorry for all the questions  :)

No problem, I like to help out when I can :)
 
I see that you were able to extract the models from the .bwm files. What format did you extract them to? Do you have the script/program to do this?
 
Imaginary Llamas said:
I see that you were able to extract the models from the .bwm files. What format did you extract them to? Do you have the script/program to do this?

I have 100s of different projects, I couldn't find my exact code but - here is some C# that will read just about enough from a BWM file for you to be able to get it into a Blender file:

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using System.Runtime.InteropServices;

namespace ModelImporter
{
    #region Structs

    public struct LHPoint
    {
        public float X;
        public float Y;
        public float Z;

        public LHPoint(float x, float y, float z)
        {
            this.X = x; this.Y = y; this.Z = z;
        }
    }

    public struct MaterialDefinition
    {
        public string Texture;
        public string Lightmap;
        public string U3;
        public string SpecularMap;
        public string U5;
        public string U6;
        public string Type;
    }

    public struct MeshDescription
    {
        public int NumFaces;
        public int IndicesPointer;
        public int U1;
        public int NumMaterialRefs;
        public int U2;
        public int ID;
        public string Name;
        public MaterialRef[] MaterialRefs;
    }

    public struct MaterialRef
    {
        public int MaterialDef;
        public int IndicesPointer;
        public int IndicesSize;
        public int VertexPointer;
        public int VertexSize;
        public int FacesPointer;
        public int FacesSize;
        public int Unknown;
    }

    public struct Bone
    {

    }

    public struct Entity
    {
        public string Name;
        public float U1X;
        public float U1Y;
        public float U1Z;
        public float U2X;
        public float U2Y;
        public float U2Z;
        public float U3X;
        public float U3Y;
        public float U3Z;
        public float PosX;
        public float PosY;
        public float PosZ;
    }

    public struct Struct4
    {
        public float X;
        public float Y;
        public float Z;
    }

    public struct Struct5
    {
        public float X;
        public float Y;
        public float Z;
    }

    public struct Strides
    {

    }

    public struct Index
    {
        public short V1;
        public short V2;
        public short V3;
    }

    [StructLayout(LayoutKind.Explicit, Size = 48)]
    public struct Vertex
    {
        [FieldOffset(0)] public float X;
        [FieldOffset(4)] public float Y;
        [FieldOffset(8)] public float Z;
        [FieldOffset(12)] public float NX;
        [FieldOffset(16)] public float NY;
        [FieldOffset(20)] public float NZ;
        [FieldOffset(24)] public float U;
        [FieldOffset(28)] public float V;
        [FieldOffset(32)] public float U1;
        [FieldOffset(36)] public float U2;
        [FieldOffset(40)] public float U3;
        [FieldOffset(44)] public float U4;
    }

    #endregion

    public class Model
    {
        #region Fields

        public MaterialDefinition[] MaterialDefinitions;
        public MeshDescription[] MeshDescriptions;
        public Bone[] Bones;
        public Entity[] Entities;
        public Struct4[] Scruct4s;
        public Struct5[] Scruct5s;
        public int[] Strides;
        public short[] Indices;
        public Vertex[][]  Verticies;
        public LHPoint[] ModelCleave;

        #endregion
    }
}

Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using Newtonsoft.Json;
using System.Runtime.InteropServices;

namespace ModelImporter
{
    static class ModelReader
    {
        public static Model LoadFromFile(Stream stream)
        {
            var reader = new BinaryReader(stream);
            reader.ReadBytes(44);

            if (reader.ReadInt32() != 721465829)
                throw new Exception("Magic number mismatch!");

            var v4 = reader.ReadInt32();
            if (v4 != 5 && v4 != 6)
                throw new Exception("fish");

            Console.WriteLine("First Long: {0}", v4);

            var headerSize = reader.ReadInt32(); // 1900
            var headerData = reader.ReadBytes(headerSize);

            var headerReader = new BinaryReader(new MemoryStream(headerData));
            headerReader.ReadBytes(68);
            var numMaterialDefinitions = headerReader.ReadInt32(); // 68
            var numMeshDescriptions = headerReader.ReadInt32(); // 72
            var numBones = headerReader.ReadInt32(); // 76
            var numEnts = headerReader.ReadInt32(); // 80
            var numSomething4 = headerReader.ReadInt32(); // 84
            var numSomething5 = headerReader.ReadInt32(); // 88
            headerReader.ReadBytes(20);
            var numVertex = headerReader.ReadInt32(); // 112
            var numStrides = headerReader.ReadInt32(); // 116
            headerReader.ReadInt32(); // 120
            var numIndices = headerReader.ReadInt32(); // 124

            var model = new Model();

            model.MaterialDefinitions = new MaterialDefinition[numMaterialDefinitions];
            Console.WriteLine("Reading {0} Material Definition(s)...", model.MaterialDefinitions.Length);
            for (int i = 0; i < model.MaterialDefinitions.Length; i++)
            {
                // there's a special case if Type = _walls_ idk what tho

                var materialDefinition = new MaterialDefinition();
                materialDefinition.Texture = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.Lightmap = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.U3 = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.SpecularMap = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.U5 = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.U6 = Util.ReadNullTerminatedString(headerReader, 64);
                materialDefinition.Type = Util.ReadNullTerminatedString(headerReader, 64);
                model.MaterialDefinitions[i] = materialDefinition;
            }

            model.MeshDescriptions = new MeshDescription[numMeshDescriptions];
            Console.WriteLine("Reading {0} Mesh Description(s)...", model.MeshDescriptions.Length);
            for (int i = 0; i < model.MeshDescriptions.Length; i++)
            {
                var meshDescription = new MeshDescription();
                meshDescription.NumFaces = headerReader.ReadInt32(); // 0
                meshDescription.IndicesPointer = headerReader.ReadInt32(); // 4
                headerReader.ReadBytes(12); // 8
                headerReader.ReadBytes(48); // 20 D3DXMATRIX ~~ ( 3x3 matrix into a 4x4? )
                headerReader.ReadBytes(16); // 68 4 32bits
                headerReader.ReadBytes(48); // 84
                meshDescription.U1 = headerReader.ReadInt32(); // 132
                meshDescription.NumMaterialRefs = headerReader.ReadInt32(); // 136
                meshDescription.U2 = headerReader.ReadInt32(); // 140  ? = *(v17 + 140) + 32 * matRef;
                meshDescription.ID = headerReader.ReadInt32(); // 144 increments 1 based, is this like an id
                meshDescription.Name = Util.ReadNullTerminatedString(headerReader, 64); // 148
                headerReader.ReadInt32(); // 212
                headerReader.ReadInt32(); // 216
                model.MeshDescriptions[i] = meshDescription;
            }

            // Material References?
            Console.WriteLine("Reading {0} Mesh Description Material References(s)...", model.MeshDescriptions.Length);
            for (int i = 0; i < model.MeshDescriptions.Length; i++)
            {
                var meshDescription = model.MeshDescriptions[i];
                meshDescription.MaterialRefs = new MaterialRef[meshDescription.NumMaterialRefs];
                for (int j = 0; j < meshDescription.NumMaterialRefs; j++)
                {
                    var materialRef = new MaterialRef();
                    materialRef.MaterialDef = headerReader.ReadInt32();
                    materialRef.IndicesPointer = headerReader.ReadInt32();
                    materialRef.IndicesSize = headerReader.ReadInt32();
                    materialRef.VertexPointer = headerReader.ReadInt32();
                    materialRef.VertexSize = headerReader.ReadInt32();
                    materialRef.FacesPointer = headerReader.ReadInt32();
                    materialRef.FacesSize = headerReader.ReadInt32();
                    materialRef.Unknown = headerReader.ReadInt32();

                    meshDescription.MaterialRefs[j] = materialRef;
                }
                model.MeshDescriptions[i] = meshDescription;
            }

            // bones maybe
            model.Bones = new Bone[numBones];
            Console.WriteLine("Reading {0} Bone(s)...", model.Bones.Length);
            for (int i = 0; i < model.Bones.Length; i++)
            {
                var bone = new Bone();
                headerReader.ReadBytes(48); // D3DXMATRIX ~~ ( 3x3 matrix into a 4x4? )
                // 12 floats in 48 bytes???
                model.Bones[i] = bone;
            }

            model.Entities = new Entity[numEnts];
            Console.WriteLine("Reading {0} Entity(s)...", model.Entities.Length);
            for (int i = 0; i < model.Entities.Length; i++)
            {
                var entity = new Entity();

                entity.U1X = headerReader.ReadSingle();
                entity.U1Y = headerReader.ReadSingle();
                entity.U1Z = headerReader.ReadSingle();
                entity.U2X = headerReader.ReadSingle();
                entity.U2Y = headerReader.ReadSingle();
                entity.U2Z = headerReader.ReadSingle();
                entity.U3X = headerReader.ReadSingle();
                entity.U3Y = headerReader.ReadSingle();
                entity.U3Z = headerReader.ReadSingle();
                entity.PosX = headerReader.ReadSingle();
                entity.PosY = headerReader.ReadSingle();
                entity.PosZ = headerReader.ReadSingle();
                entity.Name = Util.ReadNullTerminatedString(headerReader, 256);

                model.Entities[i] = entity;
            }


            model.Scruct4s = new Struct4[numSomething4];
            Console.WriteLine("Reading {0} Struct4(s)...", model.Scruct4s.Length);
            for (int i = 0; i < model.Scruct4s.Length; i++)
            {
                var struct4 = new Struct4();
                struct4.X = headerReader.ReadSingle();
                struct4.Y = headerReader.ReadSingle();
                struct4.Z = headerReader.ReadSingle();
                model.Scruct4s[i] = struct4;
            }

            model.Scruct5s = new Struct5[numSomething5];
            Console.WriteLine("Reading {0} Struct5(s)...", model.Scruct5s.Length);
            for (int i = 0; i < model.Scruct5s.Length; i++)
            {
                var struct5 = new Struct5();
                struct5.X = headerReader.ReadSingle();
                struct5.Y = headerReader.ReadSingle();
                struct5.Z = headerReader.ReadSingle();
                model.Scruct5s[i] = struct5;
            }

            // there is only ever 1 of these it seems. ( in models, idk about skins yet )
            // fishing skins use this more then once, gr8
            model.Strides = new int[numStrides];
            Console.WriteLine("Reading {0} Stride(s)...", model.Strides.Length);
            for (int i = 0; i < model.Strides.Length; i++)
            {
                var sReader = new BinaryReader(new MemoryStream(headerReader.ReadBytes(136)));// 136

                var count = sReader.ReadInt32();
                var gay = sReader.ReadInt32();
                Console.WriteLine("\tCount: {0}\tGay: {1}", count, gay);

                var totalSize = 0;
                for (int j = 0; j < count; j++)
                {
                    var format = sReader.ReadInt32();
                    var stridesize = Util.StrideInBytesFromFormat(format);

                    // single byte strides are probably bone id
                    // also weight

                    Console.WriteLine("\t\t{2}: Format: {0}\tStrideS: {1}\tGay: {3}", format, stridesize, j, sReader.ReadInt32());
                    totalSize += stridesize;
                }
                model.Strides[i] = totalSize;
            }

            model.Verticies = new Vertex[model.Strides.Length][];
            for (int i = 0; i < model.Strides.Length; i++)
            {
                model.Verticies[i] = new Vertex[numVertex];
                Console.WriteLine("Reading {0} Vertex(s)...", model.Verticies[i].Length);
                for (int j = 0; j < numVertex; j++)
                {
                    byte[] buffer = new byte[Marshal.SizeOf(typeof(Vertex))];
                    reader.Read(buffer, 0, model.Strides[i]);
                    GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
                    Vertex vertex = (Vertex)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(Vertex));
                    handle.Free();

                    model.Verticies[i][j] = vertex;
                }
            }

            model.Indices = new short[numIndices];
            Console.WriteLine("Reading {0} Indice(s)...", model.Indices.Length);
            for (int i = 0; i < model.Indices.Length; i++)
                model.Indices[i] = reader.ReadInt16();

            model.ModelCleave = new LHPoint[reader.ReadInt32()];
            Console.WriteLine("Reading {0} Model Cleave Point(s)...", model.Indices.Length);
            for (int i = 0; i < model.ModelCleave.Length; i++)
                model.ModelCleave[i] = new LHPoint(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle());

            Console.WriteLine("Remaining: {0}", reader.BaseStream.Length - reader.BaseStream.Position);

            return model;
        }
    }
}

Sorry I can't offer you anything plug and play, I wrote this in 2010 and never fully finished it. I don't have enough time to dedicate to finishing it currently, somehow should easily be able to convert this into a .obj exporter.
 
Back
Top