naev 0.12.5
nxml_lua.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
9#include "nxml_lua.h"
10
11#include "base64.h"
12#include "log.h"
13#include "mission.h"
14#include "nlua.h"
15#include "nlua_commodity.h"
16#include "nlua_faction.h"
17#include "nlua_jump.h"
18#include "nlua_outfit.h"
19#include "nlua_ship.h"
20#include "nlua_spob.h"
21#include "nlua_system.h"
22#include "nlua_time.h"
23#include "nlua_vec2.h"
24#include "utf8.h"
25
26/*
27 * Prototypes.
28 */
29static int nxml_persistDataNode( lua_State *L, xmlTextWriterPtr writer );
30static int nxml_unpersistDataNode( lua_State *L, xmlNodePtr parent );
31static int nxml_canWriteString( const char *buf, size_t len );
32
43static int nxml_saveNameAttribute( xmlTextWriterPtr writer, const char *name,
44 size_t name_len, int keynum )
45{
46 if ( nxml_canWriteString( name, name_len ) )
47 xmlw_attr( writer, "name", "%s", name );
48 else {
49 char *encoded = base64_encode_to_cstr( name, name_len );
50 xmlw_attr( writer, "name_base64", "%s", encoded );
51 free( encoded );
52 }
53 if ( keynum )
54 xmlw_attr( writer, "keynum", "1" );
55 return 0;
56}
57
70static int nxml_saveData( xmlTextWriterPtr writer, const char *type,
71 const char *name, size_t name_len, const char *value,
72 int keynum )
73{
74 xmlw_startElem( writer, "data" );
75
76 xmlw_attr( writer, "type", "%s", type );
77 nxml_saveNameAttribute( writer, name, name_len, keynum );
78 xmlw_str( writer, "%s", value );
79
80 xmlw_endElem( writer ); /* "data" */
81
82 return 0;
83}
84
96static int nxml_saveCommodity( xmlTextWriterPtr writer, const char *name,
97 size_t name_len, const Commodity *c, int keynum )
98{
99 int status = 0;
100 if ( c->name == NULL )
101 return 1;
102
103 xmlw_startElem( writer, "data" );
104
105 xmlw_attr( writer, "type", COMMODITY_METATABLE );
106 nxml_saveNameAttribute( writer, name, name_len, keynum );
107 if ( c->istemp ) {
108 xmlw_attr( writer, "temp", "%d", c->istemp );
109 xmlw_startElem( writer, "commodity" );
110 status = missions_saveTempCommodity( writer, c );
111 xmlw_endElem( writer ); /* "commodity" */
112 } else
113 xmlw_str( writer, "%s", c->name );
114 xmlw_endElem( writer ); /* "data" */
115 return status;
116}
117
121static Commodity *nxml_loadCommodity( xmlNodePtr node )
122{
123 Commodity *c;
124 int istemp;
125
126 xmlr_attr_int_def( node, "temp", istemp, 0 );
127 if ( !istemp )
128 c = commodity_get( xml_get( node ) );
129 else {
130 xmlNodePtr cur = node->xmlChildrenNode;
131 c = NULL;
132 do {
133 xml_onlyNodes( cur );
134 if ( xml_isNode( cur, "commodity" ) )
136 } while ( xml_nextNode( cur ) );
137 }
138 return c;
139}
140
153static int nxml_saveJump( xmlTextWriterPtr writer, const char *name,
154 size_t name_len, const char *start, const char *dest,
155 int keynum )
156{
157 xmlw_startElem( writer, "data" );
158
159 xmlw_attr( writer, "type", JUMP_METATABLE );
160 nxml_saveNameAttribute( writer, name, name_len, keynum );
161 xmlw_attr( writer, "dest", "%s", dest );
162 xmlw_str( writer, "%s", start );
163
164 xmlw_endElem( writer ); /* "data" */
165
166 return 0;
167}
168
176static int nxml_persistDataNode( lua_State *L, xmlTextWriterPtr writer )
177{
178 int ret;
179 char buf[32]; /* Buffer large enough for a formatted i64 (base 10). */
180 const char *name, *str, *data;
181 int keynum;
182 size_t len, name_len;
183
184 /* Default values. */
185 ret = 0;
186
187 /* We receive data in the format of: key, value */
188
189 /* key, value */
190 /* Handle different types of keys, we must not touch the stack after this
191 * operation. */
192 switch ( lua_type( L, -2 ) ) {
193 case LUA_TSTRING:
194 /* Can just tostring directly. */
195 name = lua_tolstring( L, -2, &name_len );
196 /* Isn't a number key. */
197 keynum = 0;
198 break;
199 case LUA_TNUMBER:
200 /* Can't tostring directly. */
201 lua_pushvalue( L, -2 );
202 name = lua_tolstring( L, -1, &name_len );
203 lua_pop( L, 1 ); /* Pop the new value. */
204 /* Is a number key. */
205 keynum = 1;
206 break;
207
208 /* We only handle string or number keys, so ignore the rest. */
209 default:
210 lua_pop( L, 1 ); /* key. */
211 return 0;
212 }
213 /* key, value */
214
215 /* Now handle the value. */
216 switch ( lua_type( L, -1 ) ) {
217 /* Recursive for tables. */
218 case LUA_TTABLE:
219 /* Start the table. */
220 xmlw_startElem( writer, "data" );
221 xmlw_attr( writer, "type", "table" );
222 nxml_saveNameAttribute( writer, name, name_len, keynum );
223 lua_pushnil( L ); /* key, value, nil */
224 while ( lua_next( L, -2 ) != 0 ) {
225 /* key, value, key, value */
226 ret |= nxml_persistDataNode( L, writer ); /* pops the value. */
227 /* key, value, key */
228 }
229 /* key, value */
230 xmlw_endElem( writer ); /* "table" */
231 break;
232
233 /* Normal number. */
234 case LUA_TNUMBER:
235 nxml_saveData( writer, "number", name, name_len, lua_tostring( L, -1 ),
236 keynum );
237 /* key, value */
238 break;
239
240 /* Boolean is either 1 or 0. */
241 case LUA_TBOOLEAN:
242 /* lua_tostring doesn't work on booleans. */
243 if ( lua_toboolean( L, -1 ) )
244 buf[0] = '1';
245 else
246 buf[0] = '0';
247 buf[1] = '\0';
248 nxml_saveData( writer, "bool", name, name_len, buf, keynum );
249 /* key, value */
250 break;
251
252 /* String is saved normally. */
253 case LUA_TSTRING:
254 data = lua_tolstring( L, -1, &len );
255 if ( nxml_canWriteString( data, len ) )
256 nxml_saveData( writer, "string", name, name_len, lua_tostring( L, -1 ),
257 keynum );
258 else {
259 char *encoded = base64_encode_to_cstr( data, len );
260 nxml_saveData( writer, "string_base64", name, name_len, encoded,
261 keynum );
262 free( encoded );
263 }
264 /* key, value */
265 break;
266
267 /* User data must be handled here. */
268 case LUA_TUSERDATA:
269 if ( lua_isspob( L, -1 ) ) {
270 Spob *pnt = spob_getIndex( lua_tospob( L, -1 ) );
271 if ( pnt != NULL )
272 nxml_saveData( writer, SPOB_METATABLE, name, name_len, pnt->name,
273 keynum );
274 else
275 WARN( _( "Failed to save invalid spob." ) );
276 /* key, value */
277 break;
278 } else if ( lua_issystem( L, -1 ) ) {
279 StarSystem *ss = system_getIndex( lua_tosystem( L, -1 ) );
280 if ( ss != NULL )
281 nxml_saveData( writer, SYSTEM_METATABLE, name, name_len, ss->name,
282 keynum );
283 else
284 WARN( _( "Failed to save invalid system." ) );
285 /* key, value */
286 break;
287 } else if ( lua_isfaction( L, -1 ) ) {
288 LuaFaction lf = lua_tofaction( L, -1 );
289 if ( !faction_isFaction(
290 lf ) ) /* Dynamic factions may become invalid for saving. */
291 break;
292 str = faction_name( lua_tofaction( L, -1 ) );
293 if ( str == NULL )
294 break;
295 nxml_saveData( writer, FACTION_METATABLE, name, name_len, str,
296 keynum );
297 /* key, value */
298 break;
299 } else if ( lua_isship( L, -1 ) ) {
300 const Ship *sh = lua_toship( L, -1 );
301 str = sh->name;
302 if ( str == NULL )
303 break;
304 nxml_saveData( writer, SHIP_METATABLE, name, name_len, str, keynum );
305 /* key, value */
306 break;
307 } else if ( lua_istime( L, -1 ) ) {
308 ntime_t t = *lua_totime( L, -1 );
309 snprintf( buf, sizeof( buf ), "%" PRId64, t );
310 nxml_saveData( writer, TIME_METATABLE, name, name_len, buf, keynum );
311 /* key, value */
312 break;
313 } else if ( lua_isjump( L, -1 ) ) {
314 LuaJump *lj = lua_tojump( L, -1 );
315 StarSystem *ss = system_getIndex( lj->srcid );
316 StarSystem *dest = system_getIndex( lj->destid );
317 if ( ( ss == NULL ) || ( dest == NULL ) )
318 WARN( _( "Failed to save invalid jump." ) );
319 else
320 nxml_saveJump( writer, name, name_len, ss->name, dest->name,
321 keynum );
322 } else if ( lua_iscommodity( L, -1 ) ) {
323 Commodity *com = lua_tocommodity( L, -1 );
324 if ( nxml_saveCommodity( writer, name, name_len, com, keynum ) != 0 )
325 WARN( _( "Failed to save invalid commodity." ) );
326 /* key, value */
327 break;
328 } else if ( lua_isoutfit( L, -1 ) ) {
329 const Outfit *o = lua_tooutfit( L, -1 );
330 str = o->name;
331 if ( str == NULL )
332 break;
333 nxml_saveData( writer, OUTFIT_METATABLE, name, name_len, str, keynum );
334 /* key, value */
335 break;
336 } else if ( lua_isvector( L, -1 ) ) {
337 vec2 *vec = lua_tovector( L, -1 );
338 xmlw_startElem( writer, "data" );
339 xmlw_attr( writer, "type", VECTOR_METATABLE );
340 nxml_saveNameAttribute( writer, name, name_len, keynum );
341 xmlw_attr( writer, "x", "%.16e", vec->x );
342 xmlw_attr( writer, "y", "%.16e", vec->y );
343 xmlw_attr( writer, "mod", "%.16e", vec->mod );
344 xmlw_attr( writer, "angle", "%.16e", vec->angle );
345 xmlw_endElem( writer );
346 /* key, value */
347 break;
348 }
349 /* Purpose fallthrough. */
350
351 /* Rest gets ignored, like functions, etc... */
352 default:
353 /* key, value */
354 break;
355 }
356 lua_pop( L, 1 ); /* key */
357
358 /* We must pop the value and leave only the key so it can continue iterating.
359 */
360
361 return ret;
362}
363
374int nxml_persistLua( nlua_env env, xmlTextWriterPtr writer )
375{
376 int ret = 0;
377
378 nlua_getenv( naevL, env, "mem" );
379
380 lua_pushnil( naevL ); /* nil */
381 /* str, nil */
382 while ( lua_next( naevL, -2 ) != 0 ) {
383 /* key, value */
384 ret |= nxml_persistDataNode( naevL, writer );
385 /* key */
386 }
387
388 lua_pop( naevL, 1 );
389
390 return ret;
391}
392
400static int nxml_unpersistDataNode( lua_State *L, xmlNodePtr parent )
401{
402 xmlNodePtr node;
403 char *name, *type, *buf, *num, *data;
404 size_t len;
405 int ret = 0;
406
407 node = parent->xmlChildrenNode;
408 do {
409 int failed = 0;
410 if ( xml_isNode( node, "data" ) ) {
411 /* Get general info. */
412 xmlr_attr_strd( node, "name", name );
413 xmlr_attr_strd( node, "type", type );
414 /* Check to see if key is a number. */
415 xmlr_attr_strd( node, "keynum", num );
416 if ( num != NULL ) {
417 lua_pushnumber( L, strtod( name, NULL ) );
418 free( num );
419 } else if ( name != NULL )
420 lua_pushstring( L, name );
421 else {
422 xmlr_attr_strd( node, "name_base64", name );
423 data = base64_decode_cstr( &len, name );
424 lua_pushlstring( L, data, len );
425 free( data );
426 }
427
428 /* handle data types */
429 /* Recursive tables. */
430 if ( strcmp( type, "table" ) == 0 ) {
431 xmlr_attr_strd( node, "name", buf );
432 /* Create new table. */
433 lua_newtable( L );
434 /* Save data. */
435 nxml_unpersistDataNode( L, node );
436 /* Set table. */
437 free( buf );
438 } else if ( strcmp( type, "number" ) == 0 )
439 lua_pushnumber( L, xml_getFloat( node ) );
440 else if ( strcmp( type, "bool" ) == 0 )
441 lua_pushboolean( L, xml_getInt( node ) );
442 else if ( strcmp( type, "string" ) == 0 )
443 lua_pushstring( L, xml_get( node ) );
444 else if ( strcmp( type, "string_base64" ) == 0 ) {
445 data = base64_decode_cstr( &len, xml_get( node ) );
446 lua_pushlstring( L, data, len );
447 free( data );
448 } else if ( strcmp( type, SPOB_METATABLE ) == 0 ) {
449 Spob *pnt = spob_get( xml_get( node ) );
450 if ( pnt != NULL ) {
451 lua_pushspob( L, spob_index( pnt ) );
452 } else {
453 WARN( _( "Failed to load nonexistent spob '%s'" ),
454 xml_get( node ) );
455 failed = 1;
456 }
457 } else if ( strcmp( type, SYSTEM_METATABLE ) == 0 ) {
458 StarSystem *ss = system_get( xml_get( node ) );
459 if ( ss != NULL )
460 lua_pushsystem( L, system_index( ss ) );
461 else {
462 WARN( _( "Failed to load nonexistent system '%s'" ),
463 xml_get( node ) );
464 failed = 1;
465 }
466 } else if ( strcmp( type, FACTION_METATABLE ) == 0 ) {
467 lua_pushfaction( L, faction_get( xml_get( node ) ) );
468 } else if ( strcmp( type, SHIP_METATABLE ) == 0 )
469 lua_pushship( L, ship_get( xml_get( node ) ) );
470 else if ( strcmp( type, TIME_METATABLE ) == 0 ) {
471 lua_pushtime( L, xml_getLong( node ) );
472 } else if ( strcmp( type, JUMP_METATABLE ) == 0 ) {
473 StarSystem *ss = system_get( xml_get( node ) );
474 xmlr_attr_strd( node, "dest", buf );
475 StarSystem *dest = system_get( buf );
476 if ( ( ss != NULL ) && ( dest != NULL ) ) {
477 LuaJump lj = { .srcid = ss->id, .destid = dest->id };
478 lua_pushjump( L, lj );
479 } else {
480 WARN( _( "Failed to load nonexistent jump from '%s' to '%s'" ),
481 xml_get( node ), buf );
482 failed = 1;
483 }
484 free( buf );
485 } else if ( strcmp( type, COMMODITY_METATABLE ) == 0 )
487 else if ( strcmp( type, OUTFIT_METATABLE ) == 0 )
488 lua_pushoutfit( L, outfit_get( xml_get( node ) ) );
489 else if ( strcmp( type, VECTOR_METATABLE ) == 0 ) {
490 vec2 vec;
491 xmlr_attr_float( node, "x", vec.x );
492 xmlr_attr_float( node, "y", vec.y );
493 xmlr_attr_float( node, "mod", vec.mod );
494 xmlr_attr_float( node, "angle", vec.angle );
495 lua_pushvector( L, vec );
496 } else {
497 /* There are a few types knowingly left out above. Quoting the
498 * lua_{to,push} methods, as of 2021-11-13, they are: article,
499 * audio, canvas, colour, data, file, font, linopt, pilot,
500 * pilotoutfit, shader, tex, transform.
501 * */
502 WARN( _( "Unknown Lua data type!" ) );
503 failed = 1;
504 }
505
506 /* Set field. */
507 if ( !failed )
508 lua_settable( L, -3 );
509 else
510 lua_pop( L, 1 );
511
512 /* cleanup */
513 free( type );
514 free( name );
515
516 ret |= failed;
517 }
518 } while ( xml_nextNode( node ) );
519
520 return ret;
521}
522
530int nxml_unpersistLua( nlua_env env, xmlNodePtr parent )
531{
532 int ret;
533
534 nlua_getenv( naevL, env, "mem" );
535 ret = nxml_unpersistDataNode( naevL, parent );
536 lua_pop( naevL, 1 );
537
538 return ret;
539}
540
549static int nxml_canWriteString( const char *buf, size_t len )
550{
551 for ( size_t i = 0; i < len; i++ ) {
552 if ( buf[i] == '\0' || ( buf[i] < 0x20 && buf[i] != '\t' &&
553 buf[i] != '\n' && buf[i] != '\r' ) )
554 return 0;
555 }
556 return u8_isvalid( buf, len );
557}
Commodity * commodity_get(const char *name)
Gets a commodity by name.
Definition commodity.c:151
int faction_isFaction(int f)
Checks whether or not a faction is valid.
Definition faction.c:1539
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition faction.c:331
int faction_get(const char *name)
Gets a faction ID by name.
Definition faction.c:209
int missions_saveTempCommodity(xmlTextWriterPtr writer, const Commodity *c)
Saves a temporary commodity's defintion into the current node.
Definition mission.c:1444
Commodity * missions_loadTempCommodity(xmlNodePtr cur)
Loads a temporary commodity.
Definition mission.c:1488
lua_State * naevL
Definition nlua.c:54
Commodity * lua_tocommodity(lua_State *L, int ind)
Lua bindings to interact with commodities.
Commodity ** lua_pushcommodity(lua_State *L, Commodity *commodity)
Pushes a commodity on the stack.
int lua_iscommodity(lua_State *L, int ind)
Checks to see if ind is a commodity.
LuaFaction * lua_pushfaction(lua_State *L, LuaFaction faction)
Pushes a faction on the stack.
int lua_isfaction(lua_State *L, int ind)
Checks to see if ind is a faction.
LuaFaction lua_tofaction(lua_State *L, int ind)
Gets faction at index.
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition nlua_jump.c:186
int lua_isjump(lua_State *L, int ind)
Checks to see if ind is a jump.
Definition nlua_jump.c:202
LuaJump * lua_tojump(lua_State *L, int ind)
This module allows you to handle the jumps from Lua.
Definition nlua_jump.c:95
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
int lua_isoutfit(lua_State *L, int ind)
Checks to see if ind is a outfit.
const Outfit * lua_tooutfit(lua_State *L, int ind)
Lua bindings to interact with outfits.
const Ship ** lua_pushship(lua_State *L, const Ship *ship)
Pushes a ship on the stack.
Definition nlua_ship.c:193
int lua_isship(lua_State *L, int ind)
Checks to see if ind is a ship.
Definition nlua_ship.c:209
const Ship * lua_toship(lua_State *L, int ind)
Lua bindings to interact with ships.
Definition nlua_ship.c:143
LuaSpob lua_tospob(lua_State *L, int ind)
This module allows you to handle the spobs from Lua.
Definition nlua_spob.c:152
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition nlua_spob.c:203
int lua_isspob(lua_State *L, int ind)
Checks to see if ind is a spob.
Definition nlua_spob.c:218
LuaSystem * lua_pushsystem(lua_State *L, LuaSystem sys)
Pushes a system on the stack.
LuaSystem lua_tosystem(lua_State *L, int ind)
Lua system module.
int lua_issystem(lua_State *L, int ind)
Checks to see if ind is a system.
ntime_t * lua_pushtime(lua_State *L, ntime_t time)
Pushes a time on the stack.
Definition nlua_time.c:123
int lua_istime(lua_State *L, int ind)
Checks to see if ind is a time.
Definition nlua_time.c:138
ntime_t * lua_totime(lua_State *L, int ind)
Bindings for interacting with the time.
Definition nlua_time.c:87
int lua_isvector(lua_State *L, int ind)
Checks to see if ind is a vector.
Definition nlua_vec2.c:161
vec2 * lua_tovector(lua_State *L, int ind)
Represents a 2D vector in Lua.
Definition nlua_vec2.c:119
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition nlua_vec2.c:145
int nxml_persistLua(nlua_env env, xmlTextWriterPtr writer)
Persists all the nxml Lua data.
Definition nxml_lua.c:374
static Commodity * nxml_loadCommodity(xmlNodePtr node)
Reverse of nxml_saveCommodity.
Definition nxml_lua.c:121
static int nxml_persistDataNode(lua_State *L, xmlTextWriterPtr writer)
Persists the node on the top of the stack and pops it.
Definition nxml_lua.c:176
int nxml_unpersistLua(nlua_env env, xmlNodePtr parent)
Unpersists Lua data into a table named "mem".
Definition nxml_lua.c:530
static int nxml_saveData(xmlTextWriterPtr writer, const char *type, const char *name, size_t name_len, const char *value, int keynum)
Persists Lua data.
Definition nxml_lua.c:70
static int nxml_unpersistDataNode(lua_State *L, xmlNodePtr parent)
Unpersists Lua data.
Definition nxml_lua.c:400
static int nxml_saveCommodity(xmlTextWriterPtr writer, const char *name, size_t name_len, const Commodity *c, int keynum)
Commodity-specific nxml_saveData derivative.
Definition nxml_lua.c:96
static int nxml_saveNameAttribute(xmlTextWriterPtr writer, const char *name, size_t name_len, int keynum)
Persists the key of a key/value pair.
Definition nxml_lua.c:43
static int nxml_saveJump(xmlTextWriterPtr writer, const char *name, size_t name_len, const char *start, const char *dest, int keynum)
Jump-specific nxml_saveData derivative.
Definition nxml_lua.c:153
static int nxml_canWriteString(const char *buf, size_t len)
Checks whether saving the given string (from lua_tolstring) can be saved into an XML document without...
Definition nxml_lua.c:549
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
Definition outfit.c:223
static const double c[]
Definition rng.c:256
const Ship * ship_get(const char *name)
Gets a ship based on its name.
Definition ship.c:99
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition space.c:1107
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition space.c:1038
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition space.c:1158
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition space.c:1007
Spob * spob_getIndex(int ind)
Gets spob by index.
Definition space.c:1140
int system_index(const StarSystem *sys)
Gets the index of a star system.
Definition space.c:1049
Represents a commodity.
Definition commodity.h:57
Lua jump Wrapper.
Definition nlua_jump.h:14
int destid
Definition nlua_jump.h:16
int srcid
Definition nlua_jump.h:15
A ship outfit, depends radically on the type.
Definition outfit.h:372
char * name
Definition outfit.h:373
Represents a space ship.
Definition ship.h:97
char * name
Definition ship.h:100
Represents a Space Object (SPOB), including and not limited to planets, stations, wormholes,...
Definition space.h:102
char * name
Definition space.h:105
Represents a 2d vector.
Definition vec2.h:45
double mod
Definition vec2.h:48
double y
Definition vec2.h:47
double angle
Definition vec2.h:49
double x
Definition vec2.h:46