naev 0.10.4
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
400 node = parent->xmlChildrenNode;
401 do {
402 if (xml_isNode(node,"data")) {
403 /* Get general info. */
404 xmlr_attr_strd(node,"name",name);
405 xmlr_attr_strd(node,"type",type);
406 /* Check to see if key is a number. */
407 xmlr_attr_strd(node,"keynum",num);
408 if (num != NULL) {
409 lua_pushnumber(L, strtod( name, NULL ));
410 free(num);
411 }
412 else if ( name != NULL )
413 lua_pushstring(L, name);
414 else {
415 xmlr_attr_strd( node, "name_base64", name );
416 data = base64_decode_cstr( &len, name );
417 lua_pushlstring( L, data, len );
418 free( data );
419 }
420
421 /* handle data types */
422 /* Recursive tables. */
423 if (strcmp(type,"table")==0) {
424 xmlr_attr_strd(node,"name",buf);
425 /* Create new table. */
426 lua_newtable(L);
427 /* Save data. */
429 /* Set table. */
430 free(buf);
431 }
432 else if (strcmp(type,"number")==0)
433 lua_pushnumber(L,xml_getFloat(node));
434 else if (strcmp(type,"bool")==0)
435 lua_pushboolean(L,xml_getInt(node));
436 else if (strcmp(type,"string")==0)
437 lua_pushstring(L,xml_get(node));
438 else if (strcmp( type, "string_base64" ) == 0) {
439 data = base64_decode_cstr( &len, xml_get( node ) );
440 lua_pushlstring( L, data, len );
441 free( data );
442 }
443 else if (strcmp(type,SPOB_METATABLE)==0 ||
444 (strcmp(type,"planet")==0)) { /* TODO "planet" check remove in 0.11.0. */
445 Spob *pnt = spob_get(xml_get(node));
446 if (pnt != NULL) {
447 lua_pushspob(L,spob_index(pnt));
448 }
449 else
450 WARN(_("Failed to load nonexistent spob '%s'"), xml_get(node));
451 }
452 else if (strcmp(type,SYSTEM_METATABLE)==0) {
453 StarSystem *ss = system_get(xml_get(node));
454 if (ss != NULL)
456 else
457 WARN(_("Failed to load nonexistent system '%s'"), xml_get(node));
458 }
459 else if (strcmp(type,FACTION_METATABLE)==0) {
460 lua_pushfaction(L,faction_get(xml_get(node)));
461 }
462 else if (strcmp(type,SHIP_METATABLE)==0)
463 lua_pushship(L,ship_get(xml_get(node)));
464 else if (strcmp(type,TIME_METATABLE)==0) {
465 lua_pushtime(L,xml_getLong(node));
466 }
467 else if (strcmp(type,JUMP_METATABLE)==0) {
468 StarSystem *ss = system_get(xml_get(node));
469 xmlr_attr_strd(node,"dest",buf);
470 StarSystem *dest = system_get( buf );
471 if ((ss != NULL) && (dest != NULL)) {
472 LuaJump lj = {.srcid = ss->id, .destid = dest->id};
473 lua_pushjump(L,lj);
474 }
475 else
476 WARN(_("Failed to load nonexistent jump from '%s' to '%s'"), xml_get(node), buf);
477 free(buf);
478 }
479 else if (strcmp(type,COMMODITY_METATABLE)==0)
481 else if (strcmp(type,OUTFIT_METATABLE)==0)
482 lua_pushoutfit(L,outfit_get(xml_get(node)));
483 else if (strcmp(type, VECTOR_METATABLE)==0) {
484 vec2 vec;
485 xmlr_attr_float( node, "x", vec.x );
486 xmlr_attr_float( node, "y", vec.y );
487 xmlr_attr_float( node, "mod", vec.mod );
488 xmlr_attr_float( node, "angle", vec.angle );
489 lua_pushvector( L, vec );
490 }
491 else {
492 /* There are a few types knowingly left out above. Quoting the lua_{to,push} methods, as of 2021-11-13, they are:
493 * article, audio, canvas, colour, data, file, font, linopt, pilot, pilotoutfit, shader, tex, transform.
494 * */
495 WARN(_("Unknown Lua data type!"));
496 free(type);
497 free(name);
498 lua_pop(L,1);
499 return -1;
500 }
501
502 /* Set field. */
503 lua_settable(L, -3);
504
505 /* cleanup */
506 free(type);
507 free(name);
508 }
509 } while (xml_nextNode(node));
510
511 return 0;
512}
513
521int nxml_unpersistLua( nlua_env env, xmlNodePtr parent )
522{
523 int ret;
524
525 nlua_getenv(naevL, env, "mem");
526 ret = nxml_unpersistDataNode(naevL,parent);
527 lua_pop(naevL,1);
528
529 return ret;
530}
531
540static int nxml_canWriteString( const char *buf, size_t len )
541{
542 for (size_t i = 0; i < len; i++) {
543 if ( buf[ i ] == '\0'
544 || ( buf[ i ] < 0x20 && buf[ i ] != '\t' && buf[ i ] != '\n' && buf[ i ] != '\r' ) )
545 return 0;
546 }
547 return u8_isvalid( buf, len );
548}
int faction_isFaction(int f)
Checks whether or not a faction is valid.
Definition: faction.c:1246
const char * faction_name(int f)
Gets a factions "real" (internal) name.
Definition: faction.c:304
int faction_get(const char *name)
Gets a faction ID by name.
Definition: faction.c:182
int missions_saveTempCommodity(xmlTextWriterPtr writer, const Commodity *c)
Saves a temporary commodity's defintion into the current node.
Definition: mission.c:1284
Commodity * missions_loadTempCommodity(xmlNodePtr cur)
Loads a temporary commodity.
Definition: mission.c:1329
Header file with generic functions and naev-specifics.
static char buf[NEWS_MAX_LENGTH]
Definition: news.c:45
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.
Definition: nlua_faction.c:192
int lua_isfaction(lua_State *L, int ind)
Checks to see if ind is a faction.
Definition: nlua_faction.c:208
LuaFaction lua_tofaction(lua_State *L, int ind)
Gets faction at index.
Definition: nlua_faction.c:155
LuaJump * lua_pushjump(lua_State *L, LuaJump jump)
Pushes a jump on the stack.
Definition: nlua_jump.c:182
int lua_isjump(lua_State *L, int ind)
Checks to see if ind is a jump.
Definition: nlua_jump.c:198
LuaJump * lua_tojump(lua_State *L, int ind)
This module allows you to handle the jumps from Lua.
Definition: nlua_jump.c:89
const Outfit ** lua_pushoutfit(lua_State *L, const Outfit *outfit)
Pushes a outfit on the stack.
Definition: nlua_outfit.c:160
int lua_isoutfit(lua_State *L, int ind)
Checks to see if ind is a outfit.
Definition: nlua_outfit.c:175
const Outfit * lua_tooutfit(lua_State *L, int ind)
Lua bindings to interact with outfits.
Definition: nlua_outfit.c:110
const Ship ** lua_pushship(lua_State *L, const Ship *ship)
Pushes a ship on the stack.
Definition: nlua_ship.c:164
int lua_isship(lua_State *L, int ind)
Checks to see if ind is a ship.
Definition: nlua_ship.c:180
const Ship * lua_toship(lua_State *L, int ind)
Lua bindings to interact with ships.
Definition: nlua_ship.c:114
LuaSpob lua_tospob(lua_State *L, int ind)
This module allows you to handle the spobs from Lua.
Definition: nlua_spob.c:139
LuaSpob * lua_pushspob(lua_State *L, LuaSpob spob)
Pushes a spob on the stack.
Definition: nlua_spob.c:192
int lua_isspob(lua_State *L, int ind)
Checks to see if ind is a spob.
Definition: nlua_spob.c:207
LuaSystem * lua_pushsystem(lua_State *L, LuaSystem sys)
Pushes a system on the stack.
Definition: nlua_system.c:185
LuaSystem lua_tosystem(lua_State *L, int ind)
Lua system module.
Definition: nlua_system.c:130
int lua_issystem(lua_State *L, int ind)
Checks to see if ind is a system.
Definition: nlua_system.c:201
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:155
vec2 * lua_tovector(lua_State *L, int ind)
Represents a 2D vector in Lua.
Definition: nlua_vec2.c:113
vec2 * lua_pushvector(lua_State *L, vec2 vec)
Pushes a vector on the stack.
Definition: nlua_vec2.c:139
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:521
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:540
const Outfit * outfit_get(const char *name)
Gets an outfit by name.
Definition: outfit.c:118
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:73
Spob * spob_get(const char *spobname)
Gets a spob based on its name.
Definition: space.c:1006
StarSystem * system_getIndex(int id)
Get the system by its index.
Definition: space.c:944
int spob_index(const Spob *p)
Gets the ID of a spob.
Definition: space.c:1055
StarSystem * system_get(const char *sysname)
Get the system from its name.
Definition: space.c:914
Spob * spob_getIndex(int ind)
Gets spob by index.
Definition: space.c:1038
int system_index(const StarSystem *sys)
Gets the index of a star system.
Definition: space.c:955
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:304
char * name
Definition: outfit.h:305
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:88
char * name
Definition: space.h:90
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