HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Troubles with .doo files

12-27-2006, 11:30 PM#1
wyrmlord
After taking a look at file specs for various Wc3 files, I decided to try messing with the war3map.doo file. After awhile of messing around, I finally made a program that is meant to open a .doo file, read the header and then the destructable data, and then change that into a series of CreateDestructableZ calls. (puts the code in a separate .j file). The code works for the first destructable in the file, up to the ID of the destructable (last field described in Pitzer Mike's sticky thread). I get a very large number which I'm not entirely sure of. And on the second line and lines thereafter, the outputted strings become even more and more messed-up. Here's the outputted text of the first few lines:

Code:
call CreateDestructableZ('LTlt',-9088.00,-4160.00,292.63,270.00,1.,4)
call CreateDestructableZ('lt',0.00,0.00,-30050048.00,-1821037410910208.00,1.,0)
call CreateDestructableZ('',-4160.00,304.25,4.71,48.52,1.,-972292096)
call CreateDestructableZ('',159411300868160510000000000000.00,-30050102.00,-0.00,-0.00,1.,50700)
call CreateDestructableZ('',299.38,4.71,1.02,58.51,1.,-981336064)
call CreateDestructableZ('ÀÅ',-30050078.00,-33272618564671244000000000000000000000.00,-33271840227202704000000000000000000000.00,-1.#J,1.,-1073691258)
call CreateDestructableZ('',4.71,1.00,1.00,57.01,1.,1134419968)
call CreateDestructableZ('‚Å',-0.00,-0.00,-0.00,550648208909562900000000.00,1.,-874167408)
call CreateDestructableZ('',1.01,1.01,1.01,0.00,1.,1083624421)
call CreateDestructableZ('ƒCåË',-2.69,-2.69,9610575770217395300000.00,0.00,1.,-1070841706)

And now here's my code:

Hidden information:
Code:
#include <stdio.h>

typedef char byte;

struct dooHeader{
       char FileID[4]; //Should be "W3do"
       int version;    //Should be 7 for RoC, 8 for TFT
       int subversion; //Unsure of what this is
       int amount;     //Amount of trees defined
};

struct dooTreeTFT{          //Format for Frozen Throne trees
       char TreeID[4];      //Rawcode for the tree
       int variation;       //Variation of the model used
       float X_Pos;         //X coordinate on the map
       float Y_Pos;         //Y coordinate on the map
       float Z_Pos;         //Z coordinate on the map
       float angle;         //Rotation of the tree (radians)
       float X_Scale;
       float Y_Scale;
       float Z_Scale;
       byte flags;          //0 = invisible and non-solid, 1 = visible and non-solid, 2 = normal
       byte life;           //Percentage of the tree's life
       int item_table;      //Item table pointer, -1 = no item table >=0 = item table
       int items_dropped;   //Number of item sets dropped on death
       int Tree_ID;         //Unique to each tree
};

struct dooTreeROC{          //Format for Rain of Chaos trees
       char TreeID[4];      //Rawcode for the tree
       int variation;       //Variation of the model used
       float X_Pos;         //X coordinate on the map
       float Y_Pos;         //Y coordinate on the map
       float Z_Pos;         //Z coordinate on the map
       float angle;         //Rotation of the tree (radians)
       float X_Scale;
       float Y_Scale;
       float Z_Scale;
       byte flags;          //0 = invisible and non-solid, 1 = visible and non-solid, 2 = normal
       byte life;           //Percentage of the tree's life
       int Tree_ID;         //Unique to each tree
};

float RadToDeg(float radians)
{
      return radians * 180/3.14159;
}

int main(int argc, char* argv[])
{
     FILE *fp;
     dooHeader header;

     fp = fopen(argv[1], "r");
     fread(&header, sizeof(dooHeader), 1, fp);
     
     //printf("FileID: %s Version: %d Subversion: %d Amount of Destructables: %d ", header.FileID, header.version, header.subversion, header.amount);
     //^ that line makes a loud "beep" sound
     
     if (header.version == 7)
     {
        dooTreeROC tree[header.amount];
        fread(&tree, 42, header.amount, fp);
        
        fclose(fp);
        fp = fopen("outputdoo.j", "w");
        for(int i = 0; i < header.amount; i++)
        {
                char temp[5];
                temp[0] = tree[i].TreeID[0];
                temp[1] = tree[i].TreeID[1];
                temp[2] = tree[i].TreeID[2];
                temp[3] = tree[i].TreeID[3];
                temp[4] = '\0';
                fprintf(fp, "call CreateDestructableZ('%s',%.2f,%.2f,%.2f,%.2f,1.,%d)\n", temp, tree[i].X_Pos, tree[i].Y_Pos,
                       tree[i].Z_Pos, RadToDeg(tree[i].angle), tree[i].variation);
        }
     }
     else
     {
        dooTreeTFT tree[header.amount];
        fread(&tree, sizeof(dooTreeTFT), header.amount, fp);
        
        fclose(fp);
        fp = fopen("outputdoo.j", "w");
        for(int i = 0; i < header.amount; i++)
        {
                char temp[5];
                temp[0] = tree[i].TreeID[0];
                temp[1] = tree[i].TreeID[1];
                temp[2] = tree[i].TreeID[2];
                temp[3] = tree[i].TreeID[3];
                temp[4] = '\0';
               fprintf(fp, "call CreateDestructableZ('%s',%.2f,%.2f,%.2f,%.2f,1.,%d)\n", temp, tree[i].X_Pos, tree[i].Y_Pos,
                       tree[i].Z_Pos, RadToDeg(tree[i].angle), tree[i].variation);
        }
     }
     
     fclose(fp);
     getchar();
     return 0;
}


Also, the file that was opened was a ROC war3map.doo file if that helps too.

I guess I could also attach the program, as it is only 6kb. It's a command-line program, it takes 1 argument which should be the file to open.
Attached Files
File type: zipdooReader.zip (2.7 KB)
12-28-2006, 01:15 AM#2
Vexorian
I would say that int in your compiler is not 4 bytes. Try outputting sizeof(int) or something like that, if it is 2 bytes use long int .

--

I wonder though what's the benefit for using this tool?
12-28-2006, 02:14 AM#3
PipeDream
Your compiler is aligning the structure members to boundaries of their size. The int after the char sits two bytes too far. Read the documentation to figure out how to pack the members in. For gcc, it will look something like
Code:
   byte x __attribute__ ((packed));
For borland I think there's a flag you pass to the compiler.

The large numbers are what happens when you stuff a big number like 'a' into the exponent of a floating point number.

I think you're also not inlining the structures properly. The data is all linear/consecutive as you read it in. The item table int is not a pointer-the item table actually sits in there N times. Although this is trees, so it's likely I'm misreading.

Someone was asking the other day getting the height of a destructable, I assume recovering that and other information is the intent. You could also easily measure things like how much trees are slowing down your start up time.
12-28-2006, 03:17 AM#4
wyrmlord
Quote:
Originally Posted by Vexorian
I would say that int in your compiler is not 4 bytes. Try outputting sizeof(int) or something like that, if it is 2 bytes use long int .

--

I wonder though what's the benefit for using this tool?

I checked all the sizes of int/byte/etc. and they're all the right sizes. With this tool, I'm really just trying to get some practice with binary file I/O (I've never done it before). Also, I want to find out just how much the trees slow loading time (however, doing it this way would increase the overall file size, oh well).

I'm using Dev C++ which I believe uses the gcc compiler. Now, which members of the struct do I need to pack in?

EDIT: With packing all the variables in the struct it worked... for about 80% of the file, then I get the same random stuff as before. (ROC format still). Also, here's the outputted file with the new packed version.
Attached Files
File type: raroutputdoo.rar (12.7 KB)
12-28-2006, 03:59 AM#5
PipeDream
Create a file that has just the bad tree, and figure out why the tree is bad. The output doesn't have the info I want to check, but it's clear that your reader is still ignorant in the tree-structure-isn't-fixed-size department. So check if all of the numbers that your reader depends on being zero really are zero.
12-28-2006, 12:12 PM#6
Vexorian
I actually never tried reading an struct directly from a file, and always used a wrapper for it. That's actually a necessary evil for some of the wc3 formats that are not that fixed
12-28-2006, 01:35 PM#7
wyrmlord
I've found that the reader even bugs on a map full of only 1 type of tree. It will read a majority of them, but after awhile it will fail. I made a function to check the rawcodes of the trees to make sure that they are valid, and that part is working fine. I get that the tree structures aren't all the same size, but how do I find a way around this? Also, I'm currently cycling through the .doo file to see exactly when the reader messes up, I'll post my results when I'm finished.

EDIT: The file works fine until after the 26th tree. The tree after the 26th is read in fine, until I found that the unique ID of the tree was 0. All the trees have an ID starting at 0 and counting up. After that 26th tree (ID = 25) the next ID was 0, and I'm guessing that was what threw off the rest of the file. Hopefully this information can help.

Btw, if you're reading this PizterMike, I found a 4th flag that trees get. If the flag is 3, then the tree is outside camera bounds (from what I can tell).

I'm still reading at the 'fixed size', because I'm not sure how I would do that differently. Here's a file of the information gathered into each Tree struct (RoC format) if it helps at all. Also, unless I read something wrong, the RoC format for trees is 42 bytes, unlike the TFT ones which I'll mess with later.
Attached Files
File type: txtoutputdoo.txt (7.4 KB)
12-28-2006, 04:04 PM#8
Zoxc
Here is how I read .doo files in my map editor:
Code:
  Stream.Load(MapHandle, 'war3map.doo');
  Stream.Position := 12;
  Stream.Read(Count, 4);
  TriggerData.Doodads.Capacity := Count;
  for i := 0 to Count - 1 do
   begin
     New(D);
     Stream.Read(D^, 38);

     if MapData.IsExpansion then
      begin

     Stream.Read(D.ItemTable, 4);


     Stream.Read(c, 4);
     if c > 0 then
      begin
     D.ItemSets := TList.Create;
     D.ItemSets.Capacity := c;
     Dec(c);
     for x := 0 to c do
      begin
        New(ItemSet);
        D.ItemSets.Add(ItemSet);
        ItemSet.Read(Stream);
      end;
      end;


      end;
     Stream.Read(D.IntID, 4);

     New(D.Ext);

     D.Ext.VariableType := 'desctructable';
     D.Ext.Data := D;
     D.Ext.UTF8Name :=  D.Id+' ('+IntToStr(D.IntID)+')';
     D.Ext.IsArray := False;
     D.Ext.Refers := 0;
     D.Ext.JassName := 'gg_dest_'+D.Id+'_'+StringToIntEx(D.IntID);
     TriggerData.Doodads.Add(D.Ext);
     PlacedObjects.lstDebug.Items.Add(D.Ext.JassName);
     
   end;
  Stream.Free;
         

  Dispose(Stream);

and the records:

Code:
PDoodad = ^TDoodad;
TDoodad = record
Id: array [0..3] of Char;
Variation: Integer;
X,Y,Z, Angle, sX, sY, sZ: Single;
Flags, Life:Byte;
ItemTable,IntID: Integer;
ItemSets: TList;
Ext:PVariable;
end;

  PPObject = ^TPObject;
  TPObject = record
  Id:array [0..3] of Char;
  ChanceToDropOrIndex:Integer;
  end;

  PItemSet = ^TItemSet;
  TItemSet = record
  Items: TList;
  procedure Read(Stream: TReadFile);
  end;

procedure TItemSet.Read(Stream: TReadFile);
var ItemSet:PPObject;
    Count, i: Integer;
begin
     Items := TList.Create;
     Stream.Read(Count, 4);
     Items.Capacity := Count;
     Dec(Count);
     for i := 0 to Count do
      begin
        New(ItemSet);
        Items.Add(ItemSet);
        Stream.Read(ItemSet.Id, 4);
        Stream.Read(ItemSet.ChanceToDropOrIndex, 4);
      end;
end;
12-28-2006, 06:44 PM#9
Vexorian
good luck converting that to C
12-28-2006, 08:30 PM#10
wyrmlord
Could you comment your code so I can get an idea on what does what? I have a slight understanding, but I don't know what Delphi functions do what (and I'm a bit confused with some of the syntax).

EDIT: Finally figured out what Zoxc's code did (scanned the file little by little, tree by tree), and it worked for me (RoC). Now I just need to finish the code up and this should be a working tool.
12-29-2006, 01:03 PM#11
Zoxc
Delphi ~= English so :D
12-29-2006, 09:08 PM#12
wyrmlord
My program is working fine now, but according to a post inside Pitzer Mike's sticky thread, the .doo file contains both destructables and doodads. My program is meant to find any data that can be removed and created in the map script. However, doodads cannot be created in JASS (correct me if I'm wrong), so I need some way to tell if the data is a doodad or destructable. So basically, how would I go about doing this?
12-31-2006, 11:07 AM#13
PitzerMike
Copy all destructable ids from Units\DestructableData.slk into a hashtable or binary search tree.
When parsing the doo file just look it up.

Custom ids for destructable start with B, so the first custom destructable is 'B000' whereas doodads start at 'D000'. So when you can't find the id in your lookup table and the id doesn't start with a B it's a doodad.

Here's a list of exceptions, namely doodads that start with a B:

BPtw
BPca
BObo
BOct
BOth
BOtt
BRcr
BRfs
BRrk
BRrp
BRrs
BRsp
BRgs
BSar
BSra
BSr1
BSrc
BSrv
BSrw
12-31-2006, 02:21 PM#14
wyrmlord
Okay, I've got all the IDs from the DestructableData.slk in a file. So as you're saying, all I need to do for custom ones is check if it starts with a B and isn't one of those IDs?
12-31-2006, 03:52 PM#15
PitzerMike
Exactly.
You just check if it starts with a B and is not in the list that i posted above.