Search code examples
javajsonslicktiled

MapEditor export as JSON - Object encrypted?


I have used MapEditor for my choice of map creation with my particular TileMaps that i have made. My question is this:

When exporting a created map VIA JSON, Usually it seems most map's are shown as their tile id as shown in the snippet below.

{
  "data":[1, 2, 1, 2, 3, 1, 3, 1, 2, 2, 3, 3, 4, 4, 4, 1], <------ TIle Id's
  "height":4,
  "name":"ground",
  "opacity":1,
  "properties":
     {
      "tileLayerProp":"1"
     },
  "type":"tilelayer",
  "visible":true,
  "width":4,
  "x":0,
  "y":0
}

however, when i check my own exported JSON map in the same fields i see,

 {
         "compression":"zlib",
         "data":"eJzt1UFqwzAQQFFvus1Juizdldz\/ViULgQmWItmyJMtv4JGQBirNT9NlmXO+L2qEaXHP30K9u\/RWuq9SYR4rqenZ5+xdlOzrzHlsyJ2Wbe4yWz1KmoS5W4\/UmcLPfiKPsYm10CM9qTPlnDW231SPkiaP5T49UufKbbG1308tcpqs33eHHkfPFttvbouSHmc3GWGOnK9k53uabL1vth7PwjPG1GyRa7bvq9Biz\/TY\/8w9jrR4Te8Wo\/fY871TY2ZtcWQ\/Rz\/rtUaPcVq8z\/peV2wRdvp6\/Fs9XwqejzJndWjdY\/37QpOrTOwuM\/So3ST2v77WtGjQu0etJqFF7F5HptXfQ48WqZ3tbZJqUaNJyw6j9PjU5Cvyek6LVJP312LvuWOPvedocc9ZW\/T4rF2NHuNo3UKPsVrooccV9GqhR\/\/96zFui1F6tDhj7z2P2KPXeXvveOYepXfovd879gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA4v4BxPaYbQ==",
         "encoding":"base64",
         "height":100,
         "name":"solids",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":100,
         "x":0,
         "y":0
        }, 

both of these are real samples, the first pulled from their documentation and the second from my raw export. As you can see the 'data' field of theirs differs from mine in that theirs is a JSONArray and as a result I believe this function,

public static void load(String path) throws Exception 
    {
        JSONParser parser = new JSONParser();
        Object obj = parser.parse(new FileReader(path));
        JSONObject jObj = (JSONObject) obj;

        JSONArray layers = (JSONArray) jObj.get("layers");
        int amount = layers.size();

        for (int i = 0; i < amount; i++) 
        {

            JSONObject layer = (JSONObject) layers.get(i);
            String type = (String) layer.get("name");

            if (type.equals("solids")) 
            {
                WIDTH = (int) ((long)layer.get("width"));
                HEIGHT = (int) ((long)layer.get("height"));
                solids = parse((JSONArray) layer.get("data"));
            } 

has been giving me problems. Is it because in the 'data' field of my map, i believe that is a JSONString and an encoded one at that.

After reasearching it seems I would need to decode the 'data' and somehow eventually that will turn into the 'data' field you see in the first block of code.

Any tips on how i might do this or JUST DISABLE ENCODING ALL TOGETHER?

EDIT: MY PARSE FUNCTION UPON REQUEST

private static Image[][] parse(JSONArray array)
    {
        Image[][] layer = new Image[WIDTH][HEIGHT];
        int index;

        for (int x = 0; x < WIDTH; ++x)
        {
            for (int y = 0; y < HEIGHT; ++y)
            {
                index = (int)((long)array.get((y * WIDTH) + x)); //must cast because JSON returns long
                layer[x][y] = getSpriteImage(index);
            }
        }
        return layer;
    }

EDIT 2: my editted load function

public static void load(String path) throws Exception 
    {
        JSONParser parser = new JSONParser();
        Object obj = parser.parse(new FileReader(path));
        JSONObject jObj = (JSONObject) obj;

        JSONArray layers = (JSONArray) jObj.get("layers");
        int amount = layers.size();

        for (int i = 0; i < amount; i++) 
        {

            JSONObject layer = (JSONObject) layers.get(i);
            String type = (String) layer.get("name");

            if (type.equals("solids")) 
            {
                Decoder decoder = Base64.getDecoder();
                byte[] decodedBytes = decoder.decode((byte[]) layer.get("data"));
                Inflater decompresser = new Inflater();
                solids = parse((JSONArray)decompresser.inflate(decodedBytes));

EDIT 3: my getSpriteImage function

private static Image getSpriteImage(int index)
{
    if (index == 0 )
    {
        return null;
    }

    index -= 1;

    SpriteSheet sheet = Resources.getSprite("tileset");
    int verti = sheet.getVerticalCount();
    int horiz = sheet.getHorizontalCount();

    int y = (index / verti);
    int x = (index % horiz);

    return sheet.getSubImage(x, y);
}

EDIT 5: how the map looks

[44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, etcccccccccccccccccccccccccccccccccccccccccccccccc]

EDIT 6: RENDER Method for World & GameState

world- the final value of Tile.SIZE = 5;

public static void render(float xRend, float yRend)
    {
        int offset = 2;
        int xStart = (int)(xRend / Tile.SIZE) - offset;
        int yStart = (int)(yRend / Tile.SIZE) - offset;
        int xEnd = (Window.WIDTH / Tile.SIZE) + xStart + (offset * 2);
        int yEnd = (Window.HEIGHT / Tile.SIZE) + yStart + (offset * 2);

        for (int x = xStart; x < xEnd; ++x)
        {
            for (int y = yStart; y < yEnd; ++y)
            {
                if (solidTileBound(x, y))
                {
                    solids[x][y].draw(x * Tile.SIZE, y * Tile.SIZE, Tile.SIZE, Tile.SIZE);
                }
            }
        }

    }

and GameState:

@Override
public void render(GameContainer gc, StateBasedGame stateGame, Graphics gfx) throws SlickException 
{
    gfx.drawString(("Game State"), 50, 50);

    //gfx.translate(-1500, -1500);
    World.render(500, 500);
    //gfx.resetTransform();

}

EDIT 7:

[LOG] Loading C:\Users\William.William-PC\Desktop\testMap.json
Sun Dec 06 18:51:33 EST 2015 ERROR:Error loading World
org.newdawn.slick.SlickException: Error loading World
    at main.java.TestGame.init(TestGame.java:39)
    at org.newdawn.slick.AppGameContainer.setup(AppGameContainer.java:390)
    at org.newdawn.slick.AppGameContainer.start(AppGameContainer.java:314)
    at main.java.Main.main(Main.java:40)
Caused by: java.lang.RuntimeException: Resource not found: tileset.png
    at org.newdawn.slick.util.ResourceLoader.getResourceAsStream(ResourceLoader.java:69)
    at org.newdawn.slick.opengl.InternalTextureLoader.getTexture(InternalTextureLoader.java:169)
    at org.newdawn.slick.Image.<init>(Image.java:196)
    at org.newdawn.slick.SpriteSheet.<init>(SpriteSheet.java:129)
    at org.newdawn.slick.SpriteSheet.<init>(SpriteSheet.java:115)
    at org.newdawn.slick.SpriteSheet.<init>(SpriteSheet.java:102)
    at main.java.Resources.addTileSet(Resources.java:55)
    at main.java.JSONLoader.load(JSONLoader.java:33)
    at main.java.TestGame.init(TestGame.java:31)
    ... 3 more

I looked through it thoroughly and I'm no sure where i'm supposed to be putting tileset.png or why the world won't load for that matter. Did you create your own .json export of a world to test this?


Solution

  • Very close. Unfortunately Inflater is more complicated than that:

    Instead of

                Decoder decoder = Base64.getDecoder();
                byte[] decodedBytes = decoder.decode((byte[]) layer.get("data"));
                Inflater decompresser = new Inflater();
                solids = parse((JSONArray)decompresser.inflate(decodedBytes));
    

    try this:

                Object o = layer.get("data");
    
                if ( o instanceof JSONArray )
                    solids = parseJSONArray( (JSONArray) o );
                else if ( o instanceof String 
                    && "base64".equals( layer.get( "encoding" ) )
                    && "zlib".equals( layer.get( "compression" ) )
                )
                {
                    // base64 decode
                    Decoder decoder = Base64.getDecoder();
                    byte[] decodedBytes = decoder.decode((String) o);
    
                    // zlib decompress
                    Inflater decompresser = new Inflater();
                    decompresser.setInput(decodedBytes);
                    byte[] decompressedBytes = new byte[WIDTH * HEIGHT * 4];
                    int resultLength = decompresser.inflate(decompressedBytes);
                    decompresser.end();
    
                    // convert `byte[N*4]` to `int[N]`
                    int[] result = new int[WIDTH * HEIGHT];
                    ByteBuffer bb = ByteBuffer.wrap(decompressedBytes);
                    bb.order(ByteOrder.LITTLE_ENDIAN);
                    bb.asIntBuffer().get(result);
    
                    solids = parseIntArray( result );
                }
                else
                    throw new RuntimeException( "Unimplemented data type: "
                        + o.getClass().getName() );
    

    Rename your original parse function to parseJSONArray (for instance), then copy it to a new function parseByteArray like this:

    private static Image[][] parseIntArray( int[] array )
    {
        Image[][] layer = new Image[WIDTH][HEIGHT];
    
        for (int x = 0; x < WIDTH; ++x)
            for (int y = 0; y < HEIGHT; ++y)
                layer[x][y] = getSpriteImage( array[ y * WIDTH + x ] );
    
        return layer;
    }