eniko, (edited )
@eniko@peoplemaking.games avatar

i sure hope nobody figures out that kitsune tails save files are just a proprietary binary version of json thats fairly human readable and so easy to reverse engineer and hacks them for their own purposes 🥺

kurdiumov,

@eniko why not protobuf then?

eniko,
@eniko@peoplemaking.games avatar

@kurdiumov because i wanted something simple and lightweight

datarama,
@datarama@hachyderm.io avatar

@eniko For the only (very bad) game I made as an adult (so not counting the crap I made as a kid on a C64), I just used straight-up s-expressions as my save format. :P

eniko,
@eniko@peoplemaking.games avatar

@datarama nice :D

eniko,
@eniko@peoplemaking.games avatar

oh who am i kidding i'll probably just ship a text file explaining the format with the dang game

eniko,
@eniko@peoplemaking.games avatar

"but eni why didn't you just use regular text based json if you want people to hack them"

because writing to a binary file writer generates no garbage whereas generating and concatenating a fuckton of strings generates a fuckton of garbage

carbonacat,

@eniko fuckton, fuckt object notation

ouro,

@carbonacat @eniko I think that's yaml

eniko,
@eniko@peoplemaking.games avatar

@ouro @carbonacat lmao

... wait someone should make a text based notation called "lmao"

c0dec0dec0de,
@c0dec0dec0de@hachyderm.io avatar

@eniko @ouro @carbonacat Leave Me Alone Object-notation

c0dec0dec0de,
@c0dec0dec0de@hachyderm.io avatar

@eniko @ouro @carbonacat you may have questions about why I made certain decisions in the standard for this encoding. For all inquiries, I refer you to the first three words of the name.

oblomov,
@oblomov@sociale.network avatar

@eniko see, I would have asked “why not EBML” rather

eniko,
@eniko@peoplemaking.games avatar

@oblomov because xml is an overwrought monstrosity

oblomov,
@oblomov@sociale.network avatar

@eniko
I disagree (it's not that worse than JSON, it just got overblown by getting squeezed everywhere, even when it made no sense, with the result that now everybody avoids it like the plague even where it makes sense), and I'm ready to guess you're effectively rolling your own EBML ;-)

eniko,
@eniko@peoplemaking.games avatar

@oblomov having just quickly skimmed the EBML spec, my format is significantly simpler and more human readable

eniko,
@eniko@peoplemaking.games avatar

"but eni why didn't you just use one of the existing binary json encodings"

because i looked at them all several years ago and they're all shit, actually. they're incredibly overcomplicated and in almost all cases are not actually even 1:1 compatible with regular json which like, wow, you had one fucking job

the closest i found was Universal Binary JSON which still makes several bizarre choices, not the least of which is that it uses big-endian because apparently its still 1980 and we're all using motorola 68000 cpus, but it's also so poorly maintained that several times its website got somehow hijacked by malicious actors so that makes it really hard to use or recommend

joshix,
@joshix@fosspri.de avatar

@eniko isn't big endian the correct order?

eniko,
@eniko@peoplemaking.games avatar

@joshix technically big endian is "network order" because, well, nobody knows its just what they chose for TCP/IP. but given that there are basically no big endian CPUs used in consumer computing (or most computing of any variety) and even bi-endian CPUs are run almost exclusively in little endian mode it makes no sense to make a new format that uses big endian in the modern day like the UBJson folks did. it just adds a bunch of byte shuffling overhead and makes it harder to read and write (for example C# doesn't have a built in way to read/write big endian values on a little endian system)

joshix,
@joshix@fosspri.de avatar

@eniko but big endian is imho more readable for humans. It's the for humans most natural order

eniko,
@eniko@peoplemaking.games avatar

@joshix i have to disagree. i dont know about you but i read from left to right, not right to left, and i think from small numbers to large numbers. this is not compatible with big endian and in fact the first byte of a big endian number gives me no meaningful data if i don't know what type of integer is, while i can still get the small portion of any integer by reading the first byte in little endian

0x0961h,
@0x0961h@mastodon.gamedev.place avatar

@joshix @eniko I think modern consumer-level CPUs, both x86 and x64, are little-endian

eniko,
@eniko@peoplemaking.games avatar

@0x0961h @joshix correct

eniko,
@eniko@peoplemaking.games avatar

honestly the main virtue of json is its simplicity so if your binary encoding of json starts with a header you've already fucked up big time

eniko,
@eniko@peoplemaking.games avatar

i have very strong opinions on binary json encodings

justafrog,
@justafrog@mstdn.social avatar

@eniko A random JSON-like format is still much better than the Bad Old Days, where people would make on-the-fly file formats which were basically just dumps, where position in the file determined what each byte meant.

Also, vary the offsets of all meaningful bytes based on the exact game state saved.

Store some values big-endian, others little-endian.

Maybe add a non-standard compression to make it more difficult to read.

None of that was done to obfuscate. It just kinda happened that way.

eniko,
@eniko@peoplemaking.games avatar

@justafrog tbh i quite enjoyed reverse engineering proprietary binary formats from games

justafrog,
@justafrog@mstdn.social avatar

@eniko It's no challenge, these days.

Only standard compression.

Usually it's just xml, csv or json.

Feels like they just don't care anymore.

eniko,
@eniko@peoplemaking.games avatar

@justafrog or a zip archive. gzip if you're lucky

evraire,
@evraire@mastodon.gamedev.place avatar

@eniko Did you evaluate Tyler Glaiel's GON? If so then what were your thoughts? (This is what I'm considering using for my next project.) Thanks!

https://github.com/TylerGlaiel/GON

eniko,
@eniko@peoplemaking.games avatar

@evraire looks like this is a text based format, so that's why i didn't find it when looking at binary json encodings

Farbs,
@Farbs@mastodon.social avatar

@eniko JSON inspired me to start using a data format I call "fuck it just hardcode everything".

eniko,
@eniko@peoplemaking.games avatar

@Farbs valid

mort,
@mort@fosstodon.org avatar

@eniko I wanna read about your binary JSON!

.. my save games are also a binary JSON format, but I narrowly managed to accept the things I don't like about messagepack enough to use that instead of making my own

(on the other hand, I did not manage to accept the things I don't like about the C++ messagepack libraries so I made my own)

eniko,
@eniko@peoplemaking.games avatar

@mort tbh it's super simple. objects are encoded using { and } char bytes, arrays with [ and ], and then values all have a prefix like K for keys, S for strings, T for true, F for false, B for unsigned byte, i for signed integer, or 0-9 for those values, followed by the actual value if necessary

mort,
@mort@fosstodon.org avatar

@eniko I'm curious what you think of this? https://github.com/mortie/nbon

It's not the exact same as your thing (I removed the 'K' before object keys for example) but it's very much inspired by it

eniko,
@eniko@peoplemaking.games avatar

@mort i like it! although i personally have some concerns about "Floats and doubles are distinct" and "Objects are ordered" since that fails my round-trip compatibility with json test

eniko,
@eniko@peoplemaking.games avatar

@mort this also gives me an idea for taking this further and making it truly non-binary by adding a LEB128 string size for strings so you can skip the null terminator and encoding all "binary" values as hex instead 🤔

mort,
@mort@fosstodon.org avatar

@eniko Yeah, I get that. I guess I could define float -> double conversions and int -> double conversions to be semantics-preserving...

However I like ordered objects :( My parser is streaming and I sometimes need one key of an object before another key, so I just declared objects to be ordered

Reading the IETF spec (https://datatracker.ietf.org/doc/html/rfc8259#page-6), I don't think it's specifically wrong to treat JSON objects as ordered...

eniko,
@eniko@peoplemaking.games avatar

@mort yeah i ran into that same issue in the past. what i tended to do to keep things json compatible was an array with an object inside. that way the array becomes an ordered "header" for the object, and its still round trip compatible so instead of like

{ "a": 1, "b": true, "someProp": "haha" }

i'd do it like

[ 1, true, { "someProp": "haha" }]

of course you do lose the keys on the ordered items but for things that are ordered keys shouldn't be necessary anyway

mort,
@mort@fosstodon.org avatar

@eniko That's definitely a solution, though I feel like it locks the format down a bit more; you can't just add more things to the header without breaking stuff. You also kinda lose the self-documenting property of having keys.

Another approach I've considered is to parse everything independently up-front, into some intermediate variables if necessary, and then fix up things afterward.

I guess I could add some weaselly language to the spec: implementations may treat order as significant 🤔

eniko,
@eniko@peoplemaking.games avatar

@mort there's other ways, like [{ }, { }] where the first one is the header and the 2nd is the rest

mort,
@mort@fosstodon.org avatar

@eniko I like the sound of that a lot I think. Interesting that you encode keys and strings differently, I guess that helps when inspecting the file manually; you can reset your mental parser at every "K<string>".

How do you encode keys/strings? Are they 0 terminated?

I would be tempted to use LEB128 for integers (https://en.wikipedia.org/wiki/LEB128),is that what you're doing or are they fixed width?

I wouldn't be surprised if I write up a proper spec based on these ideas some time in the future & use it

eniko,
@eniko@peoplemaking.games avatar

@mort yeah i use LEB128 and UTF-8, mostly because that's what C# uses when reading/writing strings to binary

mort,
@mort@fosstodon.org avatar

@eniko I threw together a msgpack -> enikoJSON, but with 'p' for positive LEB128-encoded integer, 'm' for negative LEB128-encoded integer, 0 terminated keys/strings, 'f' for 32-bit float, 'd' for 64-bit float, plus a 'b' for length-prefixed arbitrary binary data and 'N' for null: https://p.mort.coffee/M9S.cc

Files are slightly smaller with msgpack than this format (5927B vs 6264B; 4951B vs 5043B gzipped). But it's sooo much simpler. Hm.

eniko,
@eniko@peoplemaking.games avatar

@mort certainly a lot more readable when you're looking at the hex

breadbin,
@breadbin@bitbang.social avatar

@eniko Yes! That’s the spirit.

Let people play games the way they want. It’s ok to cheat in single player games or if everyone’s in on it.

When you release World of Kitsune Tails, then it’s time to stop cheating.

Adding a README.md would be an amazing move imho.

eniko,
@eniko@peoplemaking.games avatar

@breadbin all my games come with a readme.txt and it usually includes a command line switch to enable cheats

  • All
  • Subscribed
  • Moderated
  • Favorites
  • random
  • DreamBathrooms
  • ngwrru68w68
  • tester
  • magazineikmin
  • thenastyranch
  • rosin
  • khanakhh
  • InstantRegret
  • Youngstown
  • slotface
  • Durango
  • kavyap
  • mdbf
  • tacticalgear
  • JUstTest
  • osvaldo12
  • normalnudes
  • cubers
  • cisconetworking
  • everett
  • GTA5RPClips
  • ethstaker
  • Leos
  • provamag3
  • anitta
  • modclub
  • megavids
  • lostlight
  • All magazines