Convert Minecraft server world files into MCserver world files.
#1
I'm not quite sure how to start on this.

I've looked at
http://mc-server.org/wiki/doku.php?id=api:dataformat

and
http://www.minecraftwiki.net/wiki/Beta_Level_Format


I've never worked with this kind of data before, but it can't be that difficult to do. One simply needs to open and read from one file and format, convert and write to the other file and format.

Has someone looked into this already?
My MCServer test server is often up and running at:
OmenCraft.com


RS2K, Pilot, CEO, Gamer, Business owner and pretend programer.Tongue
Reply
Thanks given by:
#2
The MCServer file format described by the wiki is out of date, it looks more like this now:
[Image: 29818440893-orig.png]

And this isn't even complete XD

I believe the (uncompressed) chunk_data is followed by JSON describing where entities are, and is then compressed.

I never even bothered to look at how minecraft stores the world, because it just had to be shitty. But the two formats might actually look a lot like each other
Reply
Thanks given by:
#3
(10-27-2011, 08:46 AM)FakeTruth Wrote: The MCServer file format described by the wiki is out of date, it looks more like this now:
[Image: 29818440893-orig.png]

And this isn't even complete XD

I believe the (uncompressed) chunk_data is followed by JSON describing where entities are, and is then compressed.


How would I go about uncompressing the file to look at it? What format is the file in. If I could find a format that I could read I could very easily (I think) make a script that can convert it.


(10-27-2011, 08:46 AM)FakeTruth Wrote: I never even bothered to look at how minecraft stores the world, because it just had to be shitty. But the two formats might actually look a lot like each other

HAHA! So true!



The ability to convert back and forth will get a lot more people interested in this project. Big Grin

My MCServer test server is often up and running at:
OmenCraft.com


RS2K, Pilot, CEO, Gamer, Business owner and pretend programer.Tongue
Reply
Thanks given by:
#4
(10-27-2011, 09:07 AM)rs2k Wrote: How would I go about uncompressing the file to look at it? What format is the file in. If I could find a format that I could read I could very easily (I think) make a script that can convert it.
Look inside cChunkMap.cpp, that's the file that does all the loading and saving of chunks.
I actually call 'regions' 'layers', because in my mind they stacked ontop of each otherTongue but whatever.
You should look at cChunkMap::GetChunk() which deals with chunk decompressing
And you should look at cChunkMap::LoadLayer() which deals with layer/region loading

cChunkMap is overcomplicated for simple loading and saving chunks, it actually keeps the chunk's compressed data in memory until it's needed and then decompresses and loads it, it also goes the other way, when a chunk is no longer needed it is compressed in memory until the entire layer is no longer needed

(10-27-2011, 09:07 AM)rs2k Wrote: The ability to convert back and forth will get a lot more people interested in this project. Big Grin

True dat
Reply
Thanks given by:
#5
I'll take a look. I'd really like to get my world files into MCServer. It'll make it so much more interesting to debug. Big Grin



I found this on Mojang's site.
http://mod.ifies.com/f/110216/region.c
My MCServer test server is often up and running at:
OmenCraft.com


RS2K, Pilot, CEO, Gamer, Business owner and pretend programer.Tongue
Reply
Thanks given by:
#6
That file should help a lot, but you're still missing nbtGetBlocks() ^^
I once wrote an NBT parser, but deleted it after it was no longer needed (and I believe it has bugs), I guess I can dig it up if you want.

If you're wondering what NBT is, it's Notch's own invented gay ass binary tree saving thingy. It's kinda like JSON, but he just had to do it his way
Reply
Thanks given by:
#7
I found it, here it isTongue (for some reason I can't add attachments)

cNBTData.h
Code:
#pragma once

#include "MemoryLeak.h"
#include <map>
#include <list>
#include <string>

class cNBTList;
class cNBTData;

class cNBTCompound
{
public:
    cNBTCompound( cNBTCompound* a_ParentCompound ) : m_ParentCompound( a_ParentCompound ), m_CurrentList(0) { }
    virtual ~cNBTCompound() {}
public:
#ifdef _WIN32
    enum ENUM_TAG : unsigned char
#else
    enum ENUM_TAG
#endif
    {
        TAG_End = 0,
        TAG_Byte,
        TAG_Short,
        TAG_Int,
        TAG_String = 8,
        TAG_List,
        TAG_Compound,
        TAG_NumTags // Not a real tag, but contains number of tags
    };

    void Clear();

    void PutByte( std::string Name, char Value ) { m_Bytes[Name] = Value; }
    void PutShort( std::string Name, short Value ) { m_Shorts[Name] = Value; }
    void PutInteger( std::string Name, int Value ) { m_Integers[Name] = Value; }
    void PutString( std::string Name, std::string Value ) { m_Strings[Name] = Value; }
    void PutCompound( std::string Name );
    void PutList( std::string Name, ENUM_TAG Type );

    char            GetByte( std::string Name ) { return m_Bytes[Name]; }
    short            GetShort( std::string Name ) { return m_Shorts[Name]; }
    int                GetInteger( std::string Name ) { return m_Integers[Name]; }
    std::string        GetString( std::string Name ) { return m_Strings[Name]; }
    cNBTCompound*    GetCompound( std::string Name );
    cNBTList*        GetList( std::string Name ) { return m_Lists[Name]; }

    cNBTList*        GetCurrentList() { return m_CurrentList; }
    cNBTCompound*    GetParentCompound() { return m_ParentCompound; }

    bool OpenList( std::string a_Name );
    bool CloseList();

    void Serialize(std::string & a_Buffer);

    void PrintData( int a_Depth, std::string a_Name );
private:
    void AppendShort( std::string & a_Buffer, short a_Value );
    void AppendInteger( std::string & a_Buffer, int a_Value );

    cNBTCompound* m_ParentCompound;
    cNBTList*        m_CurrentList;

    typedef std::map<std::string, char>                ByteMap;
    typedef std::map<std::string, short>            ShortMap;
    typedef std::map<std::string, int>                IntegerMap;
    typedef std::map<std::string, std::string>        StringMap;
    typedef std::map<std::string, cNBTCompound*>    CompoundMap;
    typedef std::map<std::string, cNBTList*>        ListMap;
    ByteMap            m_Bytes;
    ShortMap        m_Shorts;
    IntegerMap        m_Integers;
    StringMap        m_Strings;
    CompoundMap        m_Compounds;
    ListMap            m_Lists;
};

class cNBTList
{
public:
    cNBTList( cNBTList* a_ParentList, cNBTCompound::ENUM_TAG a_Type ) : m_ParentList( a_ParentList ), m_Type( a_Type ) {}
    void AddToList( void* a_Item ) { m_List.push_back( a_Item ); }
    void* GetLastElement() { return m_List.back(); }
    cNBTCompound::ENUM_TAG GetType() { return m_Type; }
    cNBTList* GetParentList() { return m_ParentList; }

    unsigned int GetSize() { return m_List.size(); }

    void Serialize(std::string & a_Buffer);
    void PrintData(int a_Depth, std::string a_Name);
    typedef std::list<void*> VoidList;
    VoidList GetList() { return m_List; }

    void Clear();
private:
    cNBTList* m_ParentList;
    cNBTCompound::ENUM_TAG m_Type;
    VoidList m_List;
};

class cNBTData : public cNBTCompound
{
public:
    cNBTData( char* a_Buffer, unsigned int a_BufferSize );
    virtual ~cNBTData();

    void Clear();

    void PrintData();

    void ParseData();

    bool OpenCompound( std::string a_Name );
    bool CloseCompound();

    bool OpenList( std::string a_Name );
    bool CloseList();

    void PutByte( std::string Name, char Value )            { m_CurrentCompound->PutByte( Name, Value ); }
    void PutShort( std::string Name, short Value )            { m_CurrentCompound->PutShort( Name, Value ); }
    void PutInteger( std::string Name, int Value )            { m_CurrentCompound->PutInteger( Name, Value ); }
    void PutString( std::string Name, std::string Value )    { m_CurrentCompound->PutString(Name, Value); }
    void PutCompound( std::string Name )                    { m_CurrentCompound->PutCompound( Name ); }
    void PutList( std::string Name, ENUM_TAG Type )            { m_CurrentCompound->PutList( Name, Type ); }

    int                GetInteger( std::string Name )    { return m_CurrentCompound->GetInteger(Name); }
    std::string        GetString( std::string Name )    { return m_CurrentCompound->GetString(Name); }
    cNBTCompound*    GetCompound( std::string Name ) { return m_CurrentCompound->GetCompound(Name); }
    cNBTList*        GetList( std::string Name )        { return m_CurrentCompound->GetList(Name); }

    char* GetBuffer() { return m_Buffer; }
    unsigned int GetBufferSize() { return m_BufferSize; }

    void Compress();
    bool Decompress();

    void Serialize();
private:
    int m_NumUnnamedElements;
    bool m_bDecompressed;

    void ParseTags();
    void ParseCompound( bool a_bNamed );
    void ParseList( bool a_bNamed );
    void ParseString( bool a_bNamed );
    void ParseByte( bool a_bNamed );
    void ParseInt( bool a_bNamed );
    void ParseShort( bool a_bNamed );

    short ReadShort();
    std::string ReadName();
    char ReadByte();
    int ReadInt();

    cNBTCompound*    m_CurrentCompound;

    char* m_Buffer;
    unsigned int m_BufferSize;
    unsigned int m_Index;

    void (cNBTData::*m_ParseFunctions[TAG_NumTags])(bool);
};

cNBTData.cpp
Code:
#include "cNBTData.h"
#include <string> // memcpy
#include <stdio.h>
#include "zlib.h"
#include <assert.h>

#ifndef _WIN32
#include <cstring>
#include <netinet/in.h>
#endif

#ifdef _WIN32
#include <WinSock2.h>
#endif

cNBTData::~cNBTData()
{
    // TODO: Delete all compounds and stuff in it
    Clear();
}

cNBTData::cNBTData( char* a_Buffer, unsigned int a_BufferSize )
    : cNBTCompound( 0 )
{
    m_NumUnnamedElements = 0;
    for(int i = 0; i < TAG_NumTags; i++)
    {
        m_ParseFunctions[i] = 0;
    }
    m_ParseFunctions[TAG_Byte]        = &cNBTData::ParseByte;
    m_ParseFunctions[TAG_Short]        = &cNBTData::ParseShort;
    m_ParseFunctions[TAG_Int]        = &cNBTData::ParseInt;
    m_ParseFunctions[TAG_String]    = &cNBTData::ParseString;
    m_ParseFunctions[TAG_List]        = &cNBTData::ParseList;
    m_ParseFunctions[TAG_Compound]    = &cNBTData::ParseCompound;


    m_Buffer = a_Buffer;
    m_BufferSize = a_BufferSize;
    m_Index = 0;

    m_CurrentCompound = this;

    m_bDecompressed = false;
}

bool cNBTData::OpenCompound( std::string a_Name )
{
    cNBTCompound* Compound = GetCompound( a_Name );
    if( Compound )
    {
        m_CurrentCompound = Compound;
        return true;
    }
    printf("WARNING: Could not open NBT Compound %s\n", a_Name.c_str() );
    return false;
}

bool cNBTData::CloseCompound()
{
    if( m_CurrentCompound->GetParentCompound() )
    {
        m_CurrentCompound = m_CurrentCompound->GetParentCompound();
        return true;
    }
    printf("WARNING: Could not close NBT Compound, already at root!\n" );
    return false;
}

bool cNBTCompound::OpenList( std::string a_Name )
{
    if( GetList( a_Name ) )
    {
        m_CurrentList = GetList( a_Name );
        return true;
    }
    printf("WARNING: Could not open NBT List %s\n", a_Name.c_str() );
    return false;
}

bool cNBTCompound::CloseList()
{
    if( m_CurrentList )
    {
        m_CurrentList = m_CurrentList->GetParentList();
        return true;
    }
    printf("WARNING: Could not close NBT List, no list open!\n" );
    return false;
}

bool cNBTData::OpenList( std::string a_Name )
{
    return m_CurrentCompound->OpenList( a_Name );
}

bool cNBTData::CloseList()
{
    return m_CurrentCompound->CloseList();
}

void cNBTData::Compress()
{
    //printf("Before Compress size: %i\n", m_BufferSize );
    const int MAXNBTSIZE = 1024 * 2;

    int ret;
    unsigned have;
    z_stream strm;
    unsigned char* Compressed = new unsigned char[MAXNBTSIZE];

    /* allocate deflate state */
    strm.zalloc        = Z_NULL;
    strm.zfree        = Z_NULL;
    strm.opaque        = Z_NULL;
    strm.avail_in    = m_BufferSize;
    strm.avail_out    = MAXNBTSIZE;
    strm.next_in    =(Bytef*)m_Buffer;
    strm.next_out    = Compressed;
    strm.total_in    = 0;
    strm.total_out    = 0;
    ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15+MAX_WBITS, 8, Z_DEFAULT_STRATEGY);
    if (ret != Z_OK)
    {
        printf("deflateInit2 returned NOT OK\n");
        return;
    }



    /* run deflate() on input until output buffer not full, finish
       compression if all of source has been read in */

    ret = deflate(&strm, Z_FULL_FLUSH);    /* no bad return value */
    if( ret != Z_OK )
    {
        printf("WARNING: deflate returned NOT OK\n");
    }
    assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
    have = strm.total_out;

    assert(strm.avail_in == 0);     /* all input will be used */

    if( ret != Z_STREAM_END )
    {
        //printf("WARNING: Compressing didn't go to end of stream\n");
    }

    if(m_Buffer)
    {
        delete [] m_Buffer;
        m_Buffer = 0;
    }

    //printf("Compressed size: %i\n", have );

    m_BufferSize = have;
    m_Buffer = new char[ m_BufferSize ];
    memcpy( m_Buffer, Compressed, m_BufferSize );
    delete Compressed;

    /* clean up and return */
    deflateEnd(&strm);
    m_bDecompressed = false;
    return;
}

bool cNBTData::Decompress()
{
    if( m_bDecompressed )
    {
        printf("WARNING: Decompress called, while it has already been decompressed\n");
        return false;
    }
    if( m_BufferSize == 0 )
    {
        printf("WARNING: Decompress called, with m_BufferSize of 0\n");
        return false;
    }

    //printf("Before Decompress size: %i\n", m_BufferSize );

    const int MAXNBTSIZE = 1024 * 2;

    int ret;
    z_stream strm;
    unsigned char* out = new unsigned char[MAXNBTSIZE];

    /* allocate inflate state */
    strm.zalloc        = Z_NULL;
    strm.zfree        = Z_NULL;
    strm.opaque        = Z_NULL;
    strm.avail_in    = Z_NULL;
    strm.next_in    = Z_NULL;
    strm.avail_in    = m_BufferSize;
    strm.avail_out    = Z_NULL;
    strm.next_in    = (Bytef*)m_Buffer;
    strm.next_out    = Z_NULL;
    strm.avail_out    = MAXNBTSIZE;
    strm.next_out    = out;
    strm.total_in    = 0;
    strm.total_out    = 0;

    ret = inflateInit2(&strm, 16+MAX_WBITS);
    if (ret != Z_OK)
    {
        printf("inflateInit2 returned NOT OK\n");
        delete out;
        return false;
    }

    if( (ret = inflate(&strm, Z_NO_FLUSH)) != Z_STREAM_END)
    {
        assert(ret != Z_STREAM_ERROR);  /* state not clobbered */
        printf("ret != Z_STREAM_END\n");
    }
    unsigned UncompressedSize = strm.total_out; //MAXNBTSIZE - strm.avail_out;

    m_Buffer = new char[ UncompressedSize ];
    memcpy( m_Buffer, out, UncompressedSize );
    m_BufferSize = UncompressedSize;

    inflateEnd(&strm);
    delete [] out;

    if( ret != Z_STREAM_END )
    {
        printf("WARNING: NBT Data received was too big! (More than %i bytes)\n", MAXNBTSIZE);
    }

    //printf("Decompressed Size: %i\n", UncompressedSize );
    m_bDecompressed = true;
    return (ret == Z_STREAM_END) ? true : false;
}

void cNBTCompound::AppendShort( std::string & a_Buffer, short a_Value )
{
    a_Buffer.push_back( (char)((a_Value>>8)&0xff) );
    a_Buffer.push_back( (char)((a_Value)&0xff) );
}

void cNBTCompound::AppendInteger( std::string & a_Buffer, int a_Value )
{
    int NetVal = htonl( a_Value );
    a_Buffer.append( (char*)&NetVal, sizeof( int ) );
}

void cNBTCompound::Serialize(std::string & a_Buffer)
{
    //printf("cNBTCompound::Serialize()\n");
    for( CompoundMap::iterator itr = m_Compounds.begin(); itr != m_Compounds.end(); itr++ )
    {
        if( itr->second == 0 ) continue;
        a_Buffer.push_back( TAG_Compound );
        AppendShort( a_Buffer, (short)itr->first.size() );
        if( itr->first.size() > 0 )
        {
            a_Buffer.append( itr->first.c_str(), itr->first.size() );
        }
        itr->second->Serialize( a_Buffer );
        a_Buffer.push_back( TAG_End );
    }

    for( ListMap::iterator itr = m_Lists.begin(); itr != m_Lists.end(); itr++ )
    {
        if( itr->second == 0 ) continue;
        a_Buffer.push_back( TAG_List );
        AppendShort( a_Buffer, (short)itr->first.size() );
        if( itr->first.size() > 0 )
        {
            a_Buffer.append( itr->first.c_str(), itr->first.size() );
        }
        a_Buffer.push_back( (char)itr->second->GetType() );
        AppendInteger( a_Buffer, itr->second->GetSize() );
        itr->second->Serialize( a_Buffer );
    }

    for( IntegerMap::iterator itr = m_Integers.begin(); itr != m_Integers.end(); itr++ )
    {
        a_Buffer.push_back( TAG_Int );
        AppendShort( a_Buffer, (short)itr->first.size() );
        if( itr->first.size() > 0 )
        {
            a_Buffer.append( itr->first.c_str(), itr->first.size() );
        }
        AppendInteger( a_Buffer, itr->second );
    }

    for( ShortMap::iterator itr = m_Shorts.begin(); itr != m_Shorts.end(); itr++ )
    {
        a_Buffer.push_back( TAG_Short );
        AppendShort( a_Buffer, (short)itr->first.size() );
        if( itr->first.size() > 0 )
        {
            a_Buffer.append( itr->first.c_str(), itr->first.size() );
        }
        AppendShort( a_Buffer, itr->second );
    }

    for( ByteMap::iterator itr = m_Bytes.begin(); itr != m_Bytes.end(); itr++ )
    {
        a_Buffer.push_back( TAG_Byte );
        AppendShort( a_Buffer, (short)itr->first.size() );
        if( itr->first.size() > 0 )
        {
            a_Buffer.append( itr->first.c_str(), itr->first.size() );
        }
        a_Buffer.push_back( itr->second );
    }
}

void cNBTCompound::PrintData( int a_Depth, std::string a_Name )
{
    char* Prefix = new char[a_Depth*4+1];
    for(int i = 0; i < a_Depth*4; i++)
        Prefix[i] = ' ';
    Prefix[ a_Depth*4 ] = 0;

    if( a_Name.size() > 0 )
        printf("%s COMPOUND (%s)\n", Prefix, a_Name.c_str() );
    else
        printf("%s COMPOUND\n", Prefix );

    delete Prefix;
    a_Depth++;
    Prefix = new char[a_Depth*4];
    for(int i = 0; i < a_Depth*4; i++)
        Prefix[i] = ' ';
    Prefix[ a_Depth*4-1 ] = 0;

    for( CompoundMap::iterator itr = m_Compounds.begin(); itr != m_Compounds.end(); itr++ )
    {
        if( itr->second == 0 ) continue;
        itr->second->PrintData( a_Depth, itr->first );
    }

    for( ListMap::iterator itr = m_Lists.begin(); itr != m_Lists.end(); itr++)
    {
        if( itr->second == 0 ) continue;
        itr->second->PrintData( a_Depth, itr->first );
    }

    for( IntegerMap::iterator itr = m_Integers.begin(); itr != m_Integers.end(); itr++ )
    {
        printf("%s INTEGER %s (%i)\n", Prefix, itr->first.c_str(), itr->second );
    }

    for( ShortMap::iterator itr = m_Shorts.begin(); itr != m_Shorts.end(); itr++ )
    {
        printf("%s SHORT %s (%i)\n", Prefix, itr->first.c_str(), itr->second );
    }

    for( ByteMap::iterator itr = m_Bytes.begin(); itr != m_Bytes.end(); itr++ )
    {
        printf("%s BYTE %s (%i)\n", Prefix, itr->first.c_str(), itr->second );
    }

    delete Prefix;
}

void cNBTData::PrintData()
{
    printf("==== STRUCTURED NBT DATA ====\n");
    m_CurrentCompound->PrintData( 0, " " );
    printf("=============================\n");
}

void cNBTData::Serialize()
{
    std::string Buffer;
    m_CurrentCompound->Serialize( Buffer );

    if( m_Buffer )
        delete m_Buffer;
    m_Buffer = new char[Buffer.size()];
    memcpy( m_Buffer, Buffer.c_str(), Buffer.size() );
    m_BufferSize = Buffer.size();

//     for(unsigned int i = 0; i < m_BufferSize; i++)
//     {
//         printf("%02i %02x %3i %c\n", i, (unsigned char)m_Buffer[i], (unsigned char)m_Buffer[i], m_Buffer[i] );
//     }
}

void cNBTData::ParseData()
{
    if(!m_bDecompressed)
    {
        printf("WARNING: ParseData() called while data was not decompressed\n");
        return;
    }

    m_Index = 0;
//    printf("cNBTData::ParseData()\n");
//     for(unsigned int i = 0; i < m_BufferSize; i++)
//     {
//         printf("%02i %02x %3i %c\n", i, (unsigned char)m_Buffer[i], (unsigned char)m_Buffer[i], m_Buffer[i] );
//     }

    while( m_Index < m_BufferSize )
    {
        ParseTags();
    }
}

void cNBTData::ParseTags()
{
    if( m_Index < m_BufferSize )
    {
        //printf("ParseTags idx:%02i %02x %3i %c\n", m_Index, (unsigned char)m_Buffer[m_Index], (unsigned char)m_Buffer[m_Index], m_Buffer[m_Index] );
        unsigned char Tag = m_Buffer[m_Index];
        if( Tag > 0 && m_ParseFunctions[ Tag ] )
        {
            m_Index++;
            (*this.*m_ParseFunctions[ Tag ])(true);
        }
        else if( Tag == TAG_End )
        {
            m_Index++;
        }
        else
        {
            printf("UNKNOWN TAG %x\n", m_Buffer[m_Index] );
            for(unsigned int i = (m_Index-10 > 0)?m_Index-10:0 ; i < m_Index+10 && i < m_BufferSize; i++)
            {
                printf("%02i %02x %3i %c\n", i, (unsigned char)m_Buffer[i], (unsigned char)m_Buffer[i], m_Buffer[i] );
            }
            m_Index = m_BufferSize;
            return;
        }
    }
}

void cNBTData::ParseCompound( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    //printf("OPEN COMPOUND: %s\n", Name.c_str() );

    PutCompound( Name );
    OpenCompound( Name );
    while( m_Index < m_BufferSize && m_Buffer[ m_Index ] != TAG_End )
    {
        ParseTags();
    }
    CloseCompound();
    //printf("CLOSE COMPOUND\n");
}

void cNBTData::ParseList( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    ENUM_TAG TagType = (ENUM_TAG)ReadByte();
    int Length = ReadInt();
    //printf("LIST: %s Type: %02x Length: %i\n", Name.c_str(), TagType, Length );

//     for(unsigned int i = (m_Index-10 > 0)?m_Index-10:0 ; i < m_Index+10 && i < m_BufferSize; i++)
//     {
//         printf("%02i %02x %3i %c\n", i, (unsigned char)m_Buffer[i], (unsigned char)m_Buffer[i], m_Buffer[i] );
//     }

    PutList( Name, TagType );
    OpenList( Name );
    for(int i = 0; i < Length && m_Index < m_BufferSize; i++)
    {
        if( m_ParseFunctions[ TagType ] )
        {
            (*this.*m_ParseFunctions[ TagType ] )(false);
            m_Index++;
        }
    }
    CloseList();

}

void cNBTData::ParseByte( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    char Value = ReadByte();

    PutByte( Name, Value );

    //printf("BYTE: %s %i\n", Name.c_str(), Value );
}

void cNBTData::ParseShort( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    short Value = ReadShort();

    PutShort( Name, Value );

    //printf("SHORT: %s %i\n", Name.c_str(), Value );
}

void cNBTData::ParseInt( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    int Value = ReadInt();

    PutInteger( Name, Value );

    //printf("INT: %s %i\n", Name.c_str(), Value );
}

void cNBTData::ParseString( bool a_bNamed )
{
    std::string Name;
    if( a_bNamed ) Name = ReadName();
    std::string String = ReadName();

    PutString( Name, String );

    //printf("STRING: %s (%s)\n", Name.c_str(), String.c_str() );
}

std::string cNBTData::ReadName()
{
    short Length = ReadShort();

    std::string Name;
    if( Length > 0 )
    {
        for(int i = 0; i < Length; i++, m_Index++)
        {
            Name.push_back( m_Buffer[m_Index] );
        }
    }
    return Name;
}

char cNBTData::ReadByte()
{
    unsigned char Byte = m_Buffer[ m_Index ]; m_Index++;
    return Byte;
}

short cNBTData::ReadShort()
{
    short Length = 0;
    Length |= m_Buffer[ m_Index ] << 8; m_Index++;
    Length |= m_Buffer[ m_Index ]; m_Index++;
    return Length;
}

int cNBTData::ReadInt()
{
    int Value = 0;
    memcpy( &Value, m_Buffer+m_Index, sizeof(int) );
    m_Index+=sizeof(int);

    return ntohl( Value );
}

void cNBTCompound::PutList( std::string Name, ENUM_TAG Type )
{
    m_Lists[Name] = new cNBTList( m_CurrentList, Type );
}

void cNBTCompound::PutCompound( std::string Name )
{
    if( m_CurrentList )
    {
        m_CurrentList->AddToList( new cNBTCompound( this ) );
    }
    else
    {
        m_Compounds[Name] = new cNBTCompound( this );
    }
}

cNBTCompound*    cNBTCompound::GetCompound( std::string Name )
{
    if( m_CurrentList )
    {
        if( m_CurrentList->GetType() != TAG_Compound )
            return 0;

        return (cNBTCompound*)m_CurrentList->GetLastElement();
    }
    return m_Compounds[Name];
}

void cNBTList::PrintData(int a_Depth, std::string a_Name)
{
    char* Prefix = new char[a_Depth*4];
    for(int i = 0; i < a_Depth*4; i++)
        Prefix[i] = ' ';
    Prefix[ a_Depth*4-1 ] = 0;

    if( a_Name.size() > 0 )
        printf("%s LIST (%s)\n", Prefix, a_Name.c_str() );
    else
        printf("%s LIST\n", Prefix );

    delete [] Prefix;

    for( VoidList::iterator itr = m_List.begin(); itr != m_List.end(); itr++)
    {
        switch( m_Type )
        {
        case cNBTCompound::TAG_Compound:
            {
                ((cNBTCompound*)*itr)->PrintData(a_Depth+1, "...");
            }
            break;
        default:
            break;
        }
    }
}

void cNBTList::Serialize(std::string & a_Buffer)
{
    for( VoidList::iterator itr = m_List.begin(); itr != m_List.end(); itr++ )
    {
        switch( m_Type )
        {
        case cNBTCompound::TAG_Compound:
            {
                ((cNBTCompound*)(*itr))->Serialize( a_Buffer );
                a_Buffer.push_back( cNBTCompound::TAG_End );
            }
            break;
        default:
            break;
        }
    }
}

void cNBTData::Clear()
{
    while( m_CurrentCompound != this ) CloseCompound();
    m_CurrentCompound->Clear();

    if( m_Buffer )
    {
        delete m_Buffer;
        m_Buffer = 0;
    }
    m_BufferSize = 0;
}

void cNBTCompound::Clear()
{
    for( CompoundMap::iterator itr = m_Compounds.begin(); itr != m_Compounds.end(); itr++ )
    {
        if( itr->second == 0 ) continue;
        itr->second->Clear();
        delete itr->second;
        itr->second = 0;
    }
    m_Compounds.clear();

    for( ListMap::iterator itr = m_Lists.begin(); itr != m_Lists.end(); itr++ )
    {
        if( itr->second == 0 ) continue;
        itr->second->Clear();
        delete itr->second;
        itr->second = 0;
    }
    m_Lists.clear();
    m_Bytes.clear();
    m_Shorts.clear();
    m_Integers.clear();
    m_Strings.clear();
}

void cNBTList::Clear()
{
    for( VoidList::iterator itr = m_List.begin(); itr != m_List.end(); itr++)
    {
        switch( m_Type )
        {
        case cNBTCompound::TAG_Compound:
            {
                cNBTCompound* Compound = (cNBTCompound*)(*itr);
                Compound->Clear();
                delete Compound;
                *itr = 0;
            }
            break;
        case cNBTCompound::TAG_List:
            {
                cNBTList* List = (cNBTList*)(*itr);
                List->Clear();
                delete List;
                *itr = 0;
            }
            break;
        default:
            break;
        }
    }
    m_List.clear();
}
Reply
Thanks given by:
#8
Layers makes more sense than regions to me. X and Y are Lat and Long on a map which makes Z altitude.

I'm looking at cChunkMap.cpp right now. I'm determined to figure this out.Big Grin

What's the difference between ChunkMap and ChunkData and ChunkLayer?

Is a ChunkLayer the full contents of the Pak file?



I can see in cChunkLoader::LoadFormat1 in cChunkLoader.cpp that the pak location is obtained by simply dividing the chunk's coordinates by 16 (and using int and floorf).


I think I have a basic idea of how that works, I just don't know how MCServer works with the data from the file yet.
My MCServer test server is often up and running at:
OmenCraft.com


RS2K, Pilot, CEO, Gamer, Business owner and pretend programer.Tongue
Reply
Thanks given by:
#9
Don't look at cChunkLoader, it's not used at all. There's an #if 0 or something at the top.
Reply
Thanks given by:
#10
OK, I've been going through this file and finally think I understand how the data is stored.

It's a binary file. I opened up a .pak file in a hex editor and saw this:

01 01 01 00
This means Pak V1, Chunk V1, and 1 chunk in file.

16* 01 = 16, next 16 entries are the single chunk's header.
AC 00 00 00 00 00 00 00 68 0A 00 00 00 40 01 00

The rest of the data is the COMPRESSED chunk data.


01 01 A5 02
This means Pak V1, Chunk V1, and 677 chunks in file?

16 * (A5 02) = Next 10,832 entries are the chunk headers.every 16 is one chunk header

The rest of the data is the COMPRESSED chunk data. The length of the data is stored in the header of each chunk. Let's say chunk header one says the first chunk is 10,000 long. The first 10,000 chars would then be the first chunk's COMPRESSED data. Let's say the second chunk header says the next chunk is 12,000 chars long, The next 12,000 after the last 10,000 which is after the file and chunk headers is the second chunk's COMPRESSED data.

Does that sound right?
My MCServer test server is often up and running at:
OmenCraft.com


RS2K, Pilot, CEO, Gamer, Business owner and pretend programer.Tongue
Reply
Thanks given by:




Users browsing this thread: 1 Guest(s)