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