Skip to content

Instantly share code, notes, and snippets.

@nkrapivin
Last active December 22, 2021 12:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nkrapivin/8fc282f163a50bede7b2383b93f83cbe to your computer and use it in GitHub Desktop.
Save nkrapivin/8fc282f163a50bede7b2383b93f83cbe to your computer and use it in GitHub Desktop.
coded quickly in a few hours
/// @function TGINAssert(assertExpr, assertMsg)
/// @description assert() like function, do not use in your game code.
/// @param {bool} assertExpr boolean-ish exception
/// @param {string} assertMsg string that will be used if an assertion had failed.
/// @returns nothing, throws on error
function TGINAssert(assertExpr, assertMsg) {
if (!assertExpr)
throw string_replace("TGINParse: Assertion Failed: %s", "%s", string(assertMsg));
}
function TGINGroup(_name, _sprites, _spinesprites, _fonts, _tilesets, _texpages) constructor {
// public:
name/*:string*/ = _name;
sprites/*:int[]*/ = _sprites;
spinesprites/*:int[]*/ = _spinesprites;
fonts/*:int[]*/ = _fonts;
tilesets/*:int[]*/ = _tilesets;
texpages/*:int[]*/ = _texpages;
}
function TGINString(buf, offs) {
var _addr = buffer_read(buf, buffer_u32);
var _old = buffer_tell(buf);
var _str = undefined;
buffer_seek(buf, buffer_seek_start, offs + _addr);
// a string address may be legally 0 under rare cases.
if (_addr != 0) _str = buffer_read(buf, buffer_string);
buffer_seek(buf, buffer_seek_start, offs + _old);
return _str;
}
function TGINList(buf, offs, castptr) {
var _addr = buffer_read(buf, buffer_u32);
TGINAssert(_addr != 0, "YY address ptr is 0.");
var _old = buffer_tell(buf);
buffer_seek(buf, buffer_seek_start, offs + _addr);
var _cnt = buffer_read(buf, buffer_u32);
var _ret = array_create(_cnt, -1);
for (var _i = 0; _i < _cnt; ++_i) {
// asset index.
var _index = buffer_read(buf, buffer_u32);
// cast to ptr() if texture
if (is_bool(castptr) && castptr) _index = ptr(_index);
_ret[_i] = _index;
}
buffer_seek(buf, buffer_seek_start, offs + _old);
return _ret;
}
/// @function TGINParse(bufferId)
/// @description Parses the TGIN chunk. If any of the variables are undefined, the object should be discarded as invalid.
/// @param {buffer} bufferId Buffer index
/// @returns {TGINGroup[]} an array of texture group info objects
function TGINParse(bufferId) {
TGINAssert(is_numeric(bufferId) && buffer_exists(bufferId), "Buffer index is invalid or has been destroyed.");
var __found = false;
// the buffer pointed by bufferId must have it's tell set at the start of the .win (i.e. FORM)
var __winstart = buffer_tell(bufferId);
var __winlen = __winstart;
// ...and have a valid FORM. If you're RickyG who's using .win obfuscation tools
// fuck you! go edit this script if you care. I personally do not support you and won't help you in that case.
TGINAssert(buffer_read(bufferId, buffer_u32) == 0x4D524F46, "FORM is invalid.");
// length stuff...
__winlen = buffer_read(bufferId, buffer_u32);
__winlen += 8; // 'FORM' + length itself
__winstart += __winstart;
var __chunkId = 0, __chunkLen = 0;
// while not EOF
while (buffer_tell(bufferId) < __winlen) {
__chunkId = buffer_read(bufferId, buffer_u32);
__chunkLen = buffer_read(bufferId, buffer_u32);
// skip it.
if (__chunkId != 0x4E494754) {
buffer_seek(bufferId, buffer_seek_relative, __chunkLen);
continue;
}
else {
__found = true;
break;
}
}
TGINAssert(__found == true, "Unable to locate the TGIN chunk.");
TGINAssert(buffer_read(bufferId, buffer_u32) == 1, "TGIN chunk version is not one.");
var __cnt = buffer_read(bufferId, buffer_u32);
var __ret = array_create(__cnt, undefined);
for (var __i = 0; __i < __cnt; ++__i) {
var __addr = buffer_read(bufferId, buffer_u32);
TGINAssert(__addr != 0, "YY object pointer is null.");
var __pos = buffer_tell(bufferId);
buffer_seek(bufferId, buffer_seek_start, __winstart + __addr);
/* read the TGIN item here */
var __o_name = TGINString(bufferId, __winstart);
// texture page indicies are "pointers" in GameMaker
// so for compatibility reasons we ask TGINList to cast the index to a pointer.
var __o_tpag = TGINList(bufferId, __winstart, true);
var __o_sprt = TGINList(bufferId, __winstart);
var __o_spin = TGINList(bufferId, __winstart);
var __o_font = TGINList(bufferId, __winstart);
var __o_tile = TGINList(bufferId, __winstart);
buffer_seek(bufferId, buffer_seek_start, __winstart + __pos);
var __obj = new TGINGroup(__o_name, __o_sprt, __o_spin, __o_font, __o_tile, __o_tpag);
__ret[__i] = __obj;
}
// seek as if we've read an entire .win:
buffer_seek(bufferId, buffer_seek_start, __winstart + __winlen);
return __ret;
}
/*
var buf;
if (parameter_string(1) == "-game") {
buf = buffer_load(parameter_string(2));
}
else {
buf = buffer_load((os_type == os_linux)? "game.unx" : "data.win");
}
test = TGINParse(buf);
show_debug_message(test);
//[ { name : "Default", sprites : [ 0 ], spinesprites : [ ], fonts : [ 0 ], tilesets : [ 0 ], texpages : [ null ] },{ name : "texgroup1", sprites : [ 1 ], spinesprites : [ ], fonts : [ ], tilesets : [ ], texpages : [ 00000001 ] },{ name : "texgroup2", sprites : [ 2 ], spinesprites : [ ], fonts : [ ], tilesets : [ ], texpages : [ 00000002 ] },{ name : "texgroup3", sprites : [ 3 ], spinesprites : [ ], fonts : [ ], tilesets : [ ], texpages : [ 00000003 ] },{ name : "texgroup4", sprites : [ 4 ], spinesprites : [ ], fonts : [ ], tilesets : [ ], texpages : [ 00000004 ] } ]
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment