Register for your free account! | Forgot your password?

You last visited: Today at 08:25

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



PUL (Puzzle) files

Discussion on PUL (Puzzle) files within the CO2 Private Server forum part of the Conquer Online 2 category.

Closed Thread
 
Old   #1
 
Zeroxelli's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 1,769
Received Thanks: 1,142
PUL (Puzzle) files

Just wondering if anyone has any information on their structure. I wrote a basic DMap reader (took about 30 minutes, DMap/ani structure is quite simple.) and I'm trying to parse the PUL files to get the tiles for the map. It's not hard at all to get it to work if you get the offsets right, but some PUL files are half the size of the arena map (the map I worked with first) and the offsets are not absolute at all. Is there something I'm missing about calculating the offset of the tile amounts (x and y)? Of course the tile information is after them, so there's no need for those offsets to be exact.

Edit: And I know that I can calculate the number of tiles by using the tile size and coordinate width/height of the map (given a simple calculation to convert the size to isometric format), but that would still leave the offset for the tile data unknown. So I'd rather just read the data from the file the right way.

Here's an example:

As you can see, the highlighted byte (4) is the amount of tiles for the x axis, while the 4th byte to the right of it (3) is the amount of tiles for the y axis. So, 4x3 tiles, pretty simple. And the data follows immediately after that. The problem is the garbage before the tile amounts, it doesn't seem to be a consistent size, so skipping over it can be a pain. That's the only thing I need a nudge about.

Edit: Scratch that, it does seem to have some uniformity to it. Tile amount for the X axis has been at 263 for 4 maps now, and the amount for the y axis is at 267. They're both UInt16 (ushort).
Zeroxelli is offline  
Thanks
2 Users
Old 06/11/2012, 20:43   #2
 
nTL3fTy's Avatar
 
elite*gold: 0
Join Date: Jun 2005
Posts: 692
Received Thanks: 353
Taken from the EO client source:
Code:
var version = 0;
var header = reader.ReadCString(8);

if (header == "PUZZLE") version = 1;
else if (header == "PUZZLE2") version = 2;

if (version == 0)
    return null;

var aniFile = reader.ReadCString(256);

var size = reader.ReadTqSize(); // two ints (Width and Height)
file.Size = size;

var amount = file.PuzzleAmount; // size.Width * size.Height
for (var i = 0; i < amount; i++)
{
    file.PuzzleIndex[i] = reader.ReadInt16();
}

file.RollSpeedX = 0;
file.RollSpeedY = 0;

if (version == 2)
{
    file.RollSpeedX = reader.ReadInt32();
    file.RollSpeedY = reader.ReadInt32();
}
nTL3fTy is offline  
Thanks
3 Users
Old 06/11/2012, 20:51   #3
 
Zeroxelli's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 1,769
Received Thanks: 1,142
Quote:
Originally Posted by nTL3fTy View Post
Taken from the EO client source:
Code:
var version = 0;
var header = reader.ReadCString(8);

if (header == "PUZZLE") version = 1;
else if (header == "PUZZLE2") version = 2;

if (version == 0)
    return null;

var aniFile = reader.ReadCString(256);

var size = reader.ReadTqSize(); // two ints (Width and Height)
file.Size = size;

var amount = file.PuzzleAmount; // size.Width * size.Height
for (var i = 0; i < amount; i++)
{
    file.PuzzleIndex[i] = reader.ReadInt16();
}

file.RollSpeedX = 0;
file.RollSpeedY = 0;

if (version == 2)
{
    file.RollSpeedX = reader.ReadInt32();
    file.RollSpeedY = reader.ReadInt32();
}
Ah, perfect, I was off by a few bytes. Is there a public download of the EO source (unmodified) still around? I formatted my usb drive by accident.

Also, here's a screenshot of TC (newplain) at .025f scale.
Zeroxelli is offline  
Thanks
2 Users
Old 06/11/2012, 21:19   #4
 
nTL3fTy's Avatar
 
elite*gold: 0
Join Date: Jun 2005
Posts: 692
Received Thanks: 353
Quote:
Originally Posted by Zeroxelli View Post
Ah, perfect, I was off by a few bytes. Is there a public download of the EO source (unmodified) still around? I formatted my usb drive by accident.


Quote:
Originally Posted by Zeroxelli View Post
Also, here's a screenshot of TC (newplain) at .025f scale.
I notice there's no bridges.
nTL3fTy is offline  
Thanks
3 Users
Old 06/11/2012, 22:32   #5
 
elite*gold: 21
Join Date: Jul 2005
Posts: 9,193
Received Thanks: 5,376
Yahh it's a very simple file as already shown. Now you just need a full dmap structure as well as displaying scene objects and then converting between screen and game space based on zoom/camera location and mouse offset.

Are you planning on turning this into a map editor? Mine is basically just missing scene objects right now as well as the FULL dmap structure (just access info right now).
pro4never is offline  
Thanks
3 Users
Old 06/12/2012, 01:09   #6
 
unknownone's Avatar
 
elite*gold: 20
Join Date: Jun 2005
Posts: 1,013
Received Thanks: 381
Full dmap structure for anyone who wants it.

Code:
        internal enum MapObjectType
        {
            Scene = 0x01,
            TerrainObject = 0x04,
            Backdrop = 0x08,
            Effect = 0x0A,
            Sound = 0x0F
        }

        public static MapData Load(Stream file) 
        {
            MapData dmap = new MapData() {
                TerrainObjects = new List<MapTerrainObject>(),
                Scenes = new List<MapScene>(),
                Sounds = new List<MapSound>(),
                Effects = new List<Map3DEffect>()
            };
            using (BinaryReader br = new BinaryReader(file))
            {
                string dmapheader = br.ReadASCIIString(8);
                dmap.PuzzlePath = br.ReadASCIIString(260);
                dmap.Bounds = br.ReadSize();
                dmap.Cells = new MapCellCollection(dmap.Bounds) { CollectionSize = dmap.Bounds };
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                    }
                    dmap.Cells.OffsetArray.Add(br.ReadInt32());
                }
                
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        PortalType = br.ReadInt32()
                    });
                }
                Int32 objCount = br.ReadInt32();
                for (int i = 0 ; i < objCount ; i++) {
                    MapObjectType type;
                    try {
                        type = (MapObjectType)br.ReadInt32();
                    }
                    catch (EndOfStreamException ex) {
                        throw ex;
                    }
                    switch (type) {
                        case MapObjectType.Scene:
                            dmap.Scenes.Add(new MapScene() {
                                ScenePath = br.ReadASCIIString(260),
                                Location = br.ReadPoint()
                            });
                            break;
                        case MapObjectType.TerrainObject:
                            dmap.TerrainObjects.Add(new MapTerrainObject() {
                                AniPath = br.ReadASCIIString(260),
                                AniName = br.ReadASCIIString(128),
                                Location = br.ReadPoint(),
                                Size = br.ReadSize(),
                                ImageOffset = br.ReadPoint(),
                                Interval = br.ReadInt32()
                            });
                            break;
                        case MapObjectType.Effect:
                            dmap.Effects.Add(new Map3DEffect() {
                                Effect = br.ReadASCIIString(64),
                                Location = br.ReadPoint()
                            });
                            break;
                        case MapObjectType.Sound:
                            dmap.Sounds.Add(new MapSound() {
                                SoundPath = br.ReadASCIIString(260),
                                Location = br.ReadPoint(),
                                Volume = br.ReadInt32(),
                                Range = br.ReadInt32() 
                            });
                            break;
                        default:
                            throw new UnknownMapDataException();
                    }
                }
                Int32 nLayers = br.ReadInt32();
                dmap.Layers = new List<MapLayer>(nLayers);
                for (int i = 0 ; i < nLayers ; i++) {
                    MapLayer layer = new MapLayer() {
                        Backdrops = new List<MapBackdrop>(),
                        TerrainObjects = new List<MapTerrainObject>(),
                        index = br.ReadInt32(),
                        layertype = br.ReadInt32(),
                        xInt = br.ReadInt32(),
                        yInt = br.ReadInt32()
                    };
                    Int32 nItems = br.ReadInt32();
                    for (int j = 0 ; j < nItems ; j++) {
                        MapObjectType nType = (MapObjectType)br.ReadInt32();
                        switch (nType)
                        {
                            case MapObjectType.Backdrop:
                                layer.Backdrops.Add(new MapBackdrop() { 
                                    PuzzlePath = br.ReadASCIIString(260) 
                                });
                                break;
                            case MapObjectType.TerrainObject:
                                layer.TerrainObjects.Add(new MapTerrainObject() {
                                    AniPath = br.ReadASCIIString(260),
                                    AniName = br.ReadASCIIString(128),
                                    Location = br.ReadPoint(),
                                    Size = br.ReadSize(),
                                    ImageOffset = br.ReadPoint(),
                                    Interval = br.ReadInt32() 
                                });
                                break;
                            default:
                                throw new UnknownMapDataException();
                        }
                        
                    }
                    dmap.Layers.Add(layer);
                }
            }
            return dmap;
        }
unknownone is offline  
Thanks
8 Users
Old 06/12/2012, 01:26   #7
 
Zeroxelli's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 1,769
Received Thanks: 1,142
Quote:
Originally Posted by nTL3fTy View Post




I notice there's no bridges.
Yep, haven't even touched scene files yet. And thanks for the link to the EO source, I missed having it as a reference.

Quote:
Originally Posted by pro4never View Post
Yahh it's a very simple file as already shown. Now you just need a full dmap structure as well as displaying scene objects and then converting between screen and game space based on zoom/camera location and mouse offset.

Are you planning on turning this into a map editor? Mine is basically just missing scene objects right now as well as the FULL dmap structure (just access info right now).
Hmm, possibly. I just did it to get the coordinates of certain spots in maps in real-time, but being able to make/edit maps would definitely be a plus...

Quote:
Originally Posted by unknownone View Post
Full dmap structure for anyone who wants it.

Code:
        internal enum MapObjectType
        {
            Scene = 0x01,
            TerrainObject = 0x04,
            Backdrop = 0x08,
            Effect = 0x0A,
            Sound = 0x0F
        }

        public static MapData Load(Stream file) 
        {
            MapData dmap = new MapData() {
                TerrainObjects = new List<MapTerrainObject>(),
                Scenes = new List<MapScene>(),
                Sounds = new List<MapSound>(),
                Effects = new List<Map3DEffect>()
            };
            using (BinaryReader br = new BinaryReader(file))
            {
                string dmapheader = br.ReadASCIIString(8);
                dmap.PuzzlePath = br.ReadASCIIString(260);
                dmap.Bounds = br.ReadSize();
                dmap.Cells = new MapCellCollection(dmap.Bounds) { CollectionSize = dmap.Bounds };
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                    }
                    dmap.Cells.OffsetArray.Add(br.ReadInt32());
                }
                
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        PortalType = br.ReadInt32()
                    });
                }
                Int32 objCount = br.ReadInt32();
                for (int i = 0 ; i < objCount ; i++) {
                    MapObjectType type;
                    try {
                        type = (MapObjectType)br.ReadInt32();
                    }
                    catch (EndOfStreamException ex) {
                        throw ex;
                    }
                    switch (type) {
                        case MapObjectType.Scene:
                            dmap.Scenes.Add(new MapScene() {
                                ScenePath = br.ReadASCIIString(260),
                                Location = br.ReadPoint()
                            });
                            break;
                        case MapObjectType.TerrainObject:
                            dmap.TerrainObjects.Add(new MapTerrainObject() {
                                AniPath = br.ReadASCIIString(260),
                                AniName = br.ReadASCIIString(128),
                                Location = br.ReadPoint(),
                                Size = br.ReadSize(),
                                ImageOffset = br.ReadPoint(),
                                Interval = br.ReadInt32()
                            });
                            break;
                        case MapObjectType.Effect:
                            dmap.Effects.Add(new Map3DEffect() {
                                Effect = br.ReadASCIIString(64),
                                Location = br.ReadPoint()
                            });
                            break;
                        case MapObjectType.Sound:
                            dmap.Sounds.Add(new MapSound() {
                                SoundPath = br.ReadASCIIString(260),
                                Location = br.ReadPoint(),
                                Volume = br.ReadInt32(),
                                Range = br.ReadInt32() 
                            });
                            break;
                        default:
                            throw new UnknownMapDataException();
                    }
                }
                Int32 nLayers = br.ReadInt32();
                dmap.Layers = new List<MapLayer>(nLayers);
                for (int i = 0 ; i < nLayers ; i++) {
                    MapLayer layer = new MapLayer() {
                        Backdrops = new List<MapBackdrop>(),
                        TerrainObjects = new List<MapTerrainObject>(),
                        index = br.ReadInt32(),
                        layertype = br.ReadInt32(),
                        xInt = br.ReadInt32(),
                        yInt = br.ReadInt32()
                    };
                    Int32 nItems = br.ReadInt32();
                    for (int j = 0 ; j < nItems ; j++) {
                        MapObjectType nType = (MapObjectType)br.ReadInt32();
                        switch (nType)
                        {
                            case MapObjectType.Backdrop:
                                layer.Backdrops.Add(new MapBackdrop() { 
                                    PuzzlePath = br.ReadASCIIString(260) 
                                });
                                break;
                            case MapObjectType.TerrainObject:
                                layer.TerrainObjects.Add(new MapTerrainObject() {
                                    AniPath = br.ReadASCIIString(260),
                                    AniName = br.ReadASCIIString(128),
                                    Location = br.ReadPoint(),
                                    Size = br.ReadSize(),
                                    ImageOffset = br.ReadPoint(),
                                    Interval = br.ReadInt32() 
                                });
                                break;
                            default:
                                throw new UnknownMapDataException();
                        }
                        
                    }
                    dmap.Layers.Add(layer);
                }
            }
            return dmap;
        }
Ahh thank you, references are always great to have.
Zeroxelli is offline  
Thanks
2 Users
Old 06/12/2012, 10:15   #8


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,125
Received Thanks: 2,518
Just a few corrections on Sparkie's code for the structure.

The value that Sparkie stores is actually a checksum for that row.

Code:
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                    }
                    [B]dmap.Cells.OffsetArray.Add(br.ReadInt32());[/B]
                }
So here is TQ's checksum calculation in C++.

Code:
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                        [B]//dwCheckData += pLayerInfo->usMask * (pLayerInfo->usTerrain+j+1) + (pLayerInfo->sAltitude+2)*(i+1+pLayerInfo->usTerrain);
                    }
                    int ActualCheckSum = br.ReadInt32();
                    if (RowCheckSum != ActualCheckSum)
                        return;[/B]
                }
The value labelled PortalType, is actually the PortalsID within that map.

Code:
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        [B]PortalType = br.ReadInt32()[/B]
                    });
                }
This change is necessary if you want to actually use the correct method for linking up portals, so if your using an official database for example, otherwise its possible you will become confused about what values mean what with relation to the database.

Code:
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        [B]PortalID = br.ReadInt32()[/B]
                    });
                }
Korvacs is offline  
Thanks
5 Users
Old 06/12/2012, 21:32   #9
 
Zeroxelli's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 1,769
Received Thanks: 1,142
Quote:
Originally Posted by Korvacs View Post
Just a few corrections on Sparkie's code for the structure.

The value that Sparkie stores is actually a checksum for that row.

Code:
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                    }
                    [B]dmap.Cells.OffsetArray.Add(br.ReadInt32());[/B]
                }
So here is TQ's checksum calculation in C++.

Code:
                for (int j = 0 ; j < dmap.Bounds.Height ; j++) {
                    for (int i = 0 ; i < dmap.Bounds.Width ; i++) {
                        dmap.Cells[i, j] = new MapCell() {
                            Access = br.ReadInt16(),
                            Surface = br.ReadInt16(),
                            Height = br.ReadInt16()
                        };
                        [B]//dwCheckData += pLayerInfo->usMask * (pLayerInfo->usTerrain+j+1) + (pLayerInfo->sAltitude+2)*(i+1+pLayerInfo->usTerrain);
                    }
                    int ActualCheckSum = br.ReadInt32();
                    if (RowCheckSum != ActualCheckSum)
                        return;[/B]
                }
The value labelled PortalType, is actually the PortalsID within that map.

Code:
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        [B]PortalType = br.ReadInt32()[/B]
                    });
                }
This change is necessary if you want to actually use the correct method for linking up portals, so if your using an official database for example, otherwise its possible you will become confused about what values mean what with relation to the database.

Code:
                Int32 portalcount = br.ReadInt32();
                dmap.Portals = new List<MapPortal>();
                for (int i = 0 ; i < portalcount ; i++) {
                    dmap.Portals.Add(new MapPortal() {
                        Location = br.ReadPoint(),
                        [B]PortalID = br.ReadInt32()[/B]
                    });
                }
Thanks I kinda figured it had something to do with verification, didn't expect a checksum though.
Zeroxelli is offline  
Thanks
2 Users
Old 06/12/2012, 23:46   #10


 
CptSky's Avatar
 
elite*gold: 0
Join Date: Jan 2008
Posts: 1,434
Received Thanks: 1,147
There is also an implementation in my CO2_CORE_DLL which is entirely based on the one in the EO server source. But with Sparkie's implementation and Korvacs rectification, it's pretty much the full format.
CptSky is offline  
Thanks
2 Users
Old 06/13/2012, 03:02   #11
 
Zeroxelli's Avatar
 
elite*gold: 0
Join Date: May 2008
Posts: 1,769
Received Thanks: 1,142
Quote:
Originally Posted by CptSky View Post
There is also an implementation in my CO2_CORE_DLL which is entirely based on the one in the EO server source. But with Sparkie's implementation and Korvacs rectification, it's pretty much the full format.
Yeah, I've been considering using your DLL for some of my projects lately, as it does look to be quite useful.
Zeroxelli is offline  
Thanks
1 User
Closed Thread


Similar Threads Similar Threads
[MAP][PUZZLE] Brainbuster I v1.0 (neue Action / Puzzle Map)
09/09/2011 - Minecraft - 2 Replies
Brainbuster I v1.0 Hey Leute, Achtung: Die Map ist auf Englisch, wir arbeiten allerdings momentan an einer deutschen Version. hier mein erster Thread und erste Map, die von mir, Telithanor, und einem Kollegen, FW_Bluti, erstellt wurde. Ich wusste nicht recht, wo ich den Thread posten sollte; falls das hier falsch ist, bitte ich den Thread zu verschieben, danke ;) . Orginial-Thread(Englisch): click
Puzzle Piraten
01/25/2011 - General Gaming Discussion - 16 Replies
Bin Neu hier auf dem Board also erstmal hi an alle! ich Suche Cheats Für das Online game Puzzle Piraten! gibts da was? Grüße super123
Getting valid coordinates on dmaps using scene and puzzle files?
01/16/2011 - CO2 Private Server - 0 Replies
So I've successfully gotten the dmap file to fit a specific format, although the number of invalid coordinates is too great. Apparently I need to add in the object accessibility. Any information at all on objects and their structures would be appreciated.
Puzzle Pirates
03/03/2009 - General Gaming Discussion - 3 Replies
Is there a hack for puzzle pirates? www.puzzlepirates.com (like a dubloons hack or moneyhack?)



All times are GMT +2. The time now is 08:25.


Powered by vBulletin®
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2024 elitepvpers All Rights Reserved.