4

I'm using tiled to create a tile map. (Tiled is a tile map editor with support for orthogonal and isometric maps)

It saves the map in an XML file. It can use certain encoding structures:

  • regular base64
  • base64 + gzip
  • base64 + zlib
  • regular csv

Now, I've completely given up on gzip (my server gzips traffic it anyway, so no loss there) So I thought I'd try regular base64 decoding, using a base64 jquery plugin

But the data comes out all garbled, like this:

��������������������������������������������������������

I guess it's binary encoding it, but how do I get around that?

Example data that needs to be decoded (regular base64):

jQAAAI4AAACPAAAAkAAAAJEAAACSAAAAkwAAAKEAAACiAAAAowAAAKQAAAClAAAApgAAAKcAAAA=

Example data that needs to be decoded (gzipped base64):

H4sIAAAAAAAACw3DhwnAMAwAMP8P2Rdk9s1KoBQR2WK12R1Ol9vj9fn5A/luZ4Y4AAAA

Example data as csv:

141,142,143,144,145,146,147,
161,162,163,164,165,166,167

So how can I turn the regular base64 encoded bit and turn it into the csv?

Edit:

Using the solution Pointy found I got a semi-correct array. After a few thousand characters the 2 numbers would be wrong again, though. And even more frequent after that.

I then found someone who also uses tiled and the base64 encoding in his scheme. After he decoded the array, he also did this to it:

        var d = base64_decode($(this).find('data').text());
    var e = new Array();
    for (var i = 0; i <= d.length; i += 4) {
    var f = d[i] | d[i + 1] << 8 | d[i + 2] << 16 | d[i + 3] << 24;
    e.push(f)
    }

I have no idea why this is needed, but at least it works. If anyone could explain, please do!

Jelle De Loecker
  • 20,999
  • 27
  • 100
  • 142

2 Answers2

3

Try looking at the returned string character by character. In other words, get the "charCodeAt" for each character in the array.

function codesFromString(str) {
  var rv = [];
  for (var i = 0; i < str.length; ++i)
    rv.push(str.charCodeAt(i));
  }
  return rv;
}

That leaves you with an array of numbers. If you want to dump that out as CSV on a page or something, you can use join()

var csv = codeFromString(decoded).join(',');

edit — ok here's a base64 numeric decoder:

  var base64chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split("");
  var base64inv = {}; 
  for (var i = 0; i < base64chars.length; i++) { 
    base64inv[base64chars[i]] = i; 
  }
  function decodeNumeric(s) {
    s = s.replace(new RegExp('[^'+base64chars.join("")+'=]', 'g'), "");

    var p = (s.charAt(s.length-1) == '=' ? 
            (s.charAt(s.length-2) == '=' ? 'AA' : 'A') : ""); 
    var r = []; 
    s = s.substr(0, s.length - p.length) + p;

    for (var c = 0; c < s.length; c += 4) {
      var n = (base64inv[s.charAt(c)] << 18) + (base64inv[s.charAt(c+1)] << 12) +
              (base64inv[s.charAt(c+2)] << 6) + base64inv[s.charAt(c+3)];

      r.push((n >>> 16) & 255);
      r.push((n >>> 8) & 255);
      r.push(n & 255);
    }
    return r;
  }

That works on your sample, but it demonstrates that what you've written as the result is not actually correct. Instead of "141,142,143,..." it's "141,0,0,0,142,0,0,0,143,0,0,0" etc. That's what those runs of "AAA" in the encoded string are.

(code stolen from http://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64 )

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • That worked. Only every fourth number in the array is correct, though. I can iterate through that, no problem. However, after a certain amount of numbers they're wrong again. Like: 41,42,43,44,44,41,44,41,4096,12288 should be: 41,42,43,44,44,41,44,41,225,226,227 How did that happen? – Jelle De Loecker Jul 14 '10 at 17:21
  • 1
    Hmm well it could be that the base-64 data is being decoded but then the Unicode nature of String values is getting in your way. Since you really want to treat the bytes as raw numeric values, you might be better off with a custom decoder. I was playing with a Javascript base-64 decoder, it so happens, just a few days ago. I'll look for it. – Pointy Jul 14 '10 at 17:44
  • Allright, thanks! So, to clarify: the first 522 numbers are correct. – Jelle De Loecker Jul 14 '10 at 18:05
  • Almost there! It seems much more correct time, but unwanted bits still sneak in. The first time this happens is after 623 characters (without the unwanted 0) This is the way it should go: 14,11,255,256,257,11,12,13,14,14 and this is what it turned in to: 14,11,255,0,1,11,12,13,14,14 So for a small decode this works perfectly, but it gives strange results when things get bigger. – Jelle De Loecker Jul 14 '10 at 19:03
  • Well if the values are encoded properly, I'm not sure how this can not work. – Pointy Jul 14 '10 at 19:53
  • Also note that you can't encode the number 256 in a single byte! The result numbers will be in the range 0 to 255. That's all that a byte can hold. – Pointy Jul 14 '10 at 19:54
  • Oh, so that was the problem!? Because 256 would become 0, 257 1, ... It just looped around? I found something that fixed (and added it to the question), but I don't really know how it fixed it. – Jelle De Loecker Jul 14 '10 at 21:22
  • Yes. Base64 encoding is all about encoding 8-bit values (0 ... 255). Thus it's impossible to get anything *out* of an encoded string except a series of 8-bit values. – Pointy Jul 14 '10 at 21:28
  • Well, doing ` for (var i = 0; i <= source.length; i += 4) { var tempTile = source[i] | source[i + 1] << 8 | source[i + 2] << 16 | source[i + 3] << 24; destination.push(tempTile) }` on the result of the decode fixed it ... – Jelle De Loecker Jul 18 '10 at 00:51
0

old topic, I know, but the answer for your last question is here: http://sourceforge.net/apps/mediawiki/tiled/index.php?title=Examining_the_map_format.

Kleber
  • 942
  • 1
  • 15
  • 25