diff options
-rw-r--r-- | Changelog-Trunk.txt | 4 | ||||
-rw-r--r-- | src/common/utils.c | 35 | ||||
-rw-r--r-- | src/common/utils.h | 8 | ||||
-rw-r--r-- | src/map/script.c | 824 | ||||
-rw-r--r-- | src/map/script.h | 6 |
5 files changed, 574 insertions, 303 deletions
diff --git a/Changelog-Trunk.txt b/Changelog-Trunk.txt index d8f618e5a..029f66b7d 100644 --- a/Changelog-Trunk.txt +++ b/Changelog-Trunk.txt @@ -3,6 +3,10 @@ Date Added AS OF SVN REV. 5091, WE ARE NOW USING TRUNK. ALL UNTESTED BUGFIXES/FEATURES GO INTO TRUNK. IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK. +2007/06/26 + * Changed the string hash of the script engine to SDBM. + * Reporting information about script data when an error occurs. + * More work on ticket #41 (array functions). [FlavioJS] 2007/06/22 * Changed behavior of some guild-related code (according to X.4 tests): - removed code that prevented Homunculus Resurrection during WoE diff --git a/src/common/utils.c b/src/common/utils.c index b738d31c8..00b6dc290 100644 --- a/src/common/utils.c +++ b/src/common/utils.c @@ -294,30 +294,35 @@ void findfile(const char *p, const char *pat, void (func)(const char*)) } #endif -unsigned char GetByte(unsigned long val, size_t num) +uint8 GetByte(uint32 val, size_t num) { - switch(num) { - case 0: return (unsigned char)((val & 0x000000FF) ); - case 1: return (unsigned char)((val & 0x0000FF00)>>0x08); - case 2: return (unsigned char)((val & 0x00FF0000)>>0x10); - case 3: return (unsigned char)((val & 0xFF000000)>>0x18); + switch( num ) + { + case 0: return (uint8)((val & 0x000000FF) ); + case 1: return (uint8)((val & 0x0000FF00) >> 0x08); + case 2: return (uint8)((val & 0x00FF0000) >> 0x10); + case 3: return (uint8)((val & 0xFF000000) >> 0x18); default: return 0; //better throw something here } } -unsigned short GetWord(unsigned long val, size_t num) +uint16 GetWord(uint32 val, size_t num) { - switch(num) { - case 0: return (unsigned short)((val & 0x0000FFFF) ); - case 1: return (unsigned short)((val & 0xFFFF0000)>>0x10); + switch( num ) + { + case 0: return (uint16)((val & 0x0000FFFF) ); + case 1: return (uint16)((val & 0xFFFF0000) >> 0x10); default: return 0; //better throw something here } } -unsigned short MakeWord(unsigned char byte0, unsigned char byte1) +uint16 MakeWord(uint8 byte0, uint8 byte1) { - return byte0 | (byte1<<0x08); + return + ((uint16)(byte0 ))| + ((uint16)(byte1 << 0x08)); } -unsigned long MakeDWord(unsigned short word0, unsigned short word1) +uint32 MakeDWord(uint16 word0, uint16 word1) { - return ((unsigned long)word0) - | ((unsigned long)word1<<0x10); + return + ((uint32)(word0 ))| + ((uint32)(word1 << 0x10)); } diff --git a/src/common/utils.h b/src/common/utils.h index ba02fc5f1..66ce04c99 100644 --- a/src/common/utils.h +++ b/src/common/utils.h @@ -38,9 +38,9 @@ void findfile(const char *p, const char *pat, void (func)(const char*)); // byte word dword access [Shinomori] ////////////////////////////////////////////////////////////////////////// -extern unsigned char GetByte(unsigned long val, size_t num); -extern unsigned short GetWord(unsigned long val, size_t num); -extern unsigned short MakeWord(unsigned char byte0, unsigned char byte1); -extern unsigned long MakeDWord(unsigned short word0, unsigned short word1); +extern uint8 GetByte(uint32 val, size_t num); +extern uint16 GetWord(uint32 val, size_t num); +extern uint16 MakeWord(uint8 byte0, uint8 byte1); +extern uint32 MakeDWord(uint16 word0, uint16 word1); #endif /* _UTILS_H_ */ diff --git a/src/map/script.c b/src/map/script.c index e44e426ca..54f9e6c46 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -5,6 +5,7 @@ //#define DEBUG_DISP //#define DEBUG_DISASM //#define DEBUG_RUN +//#define DEBUG_HASH #include "../common/cbasetypes.h" #include "../common/socket.h" @@ -82,7 +83,7 @@ #define script_getnum(st,val) conv_num(st, script_getdata(st,val)) #define script_getstr(st,val) conv_str(st, script_getdata(st,val)) -#define script_getref(st,val) ((st)->stack->stack_data[(st)->start+(val)].ref) +#define script_getref(st,val) ( data_varref(script_getdata(st,val)) ) // Note: "top" functions/defines use indexes relative to the top of the stack // -1 is the index of the data at the top @@ -109,8 +110,18 @@ /// Returns if the script data is an internal script function label #define data_isfunclabel(data) ( (data)->type == C_USERFUNC_POS ) -/// Returns the name of the reference -#define data_referencename(data) ( str_buf + str_data[(data)->u.num&0x00ffffff].str ) +/// Returns the unique id of the variable (id and index) +#define data_varuid(data) ( (data)->u.num ) +/// Returns the id of the variable +#define data_varid(data) ( (int32)(data_varuid(data) & 0x00ffffff) ) +/// Returns the array index of the variable +#define data_varindex(data) ( (int32)(((uint32)(data_varuid(data) & 0xff000000)) >> 24) ) +/// Returns the name of the variable +#define data_varname(data) ( str_buf + str_data[data_varid(data)].str ) +/// Returns the reference of the variable (linked list of variables, can be NULL) +#define data_varref(data) ( (data)->ref ) + +#define variable_uid(id,idx) ( (int32)((((uint32)(id)) & 0x00ffffff) | (((uint32)(idx)) << 24)) ) #define FETCH(n, t) \ if( script_hasdata(st,n) ) \ @@ -141,7 +152,7 @@ int str_num=LABEL_START,str_data_size; #define SCRIPT_HASH_SIZE 1021 int str_hash[SCRIPT_HASH_SIZE]; //#define SCRIPT_HASH_DJB2 -//#define SCRIPT_HASH_SDBM +#define SCRIPT_HASH_SDBM //#define SCRIPT_HASH_ELF //#define SCRIPT_HASH_PJW @@ -223,7 +234,9 @@ extern struct script_function { } buildin_func[]; static struct linkdb_node *sleep_db; -#define not_server_variable(prefix) (prefix != '$' && prefix != '.') +#define not_server_variable(prefix) ( (prefix) != '$' && (prefix) != '.') +#define not_array_variable(prefix) ( (prefix) != '$' && (prefix) != '@' && (prefix) != '.' ) +#define is_string_variable(name) ( (name)[strlen(name) - 1] == '$' ) /*========================================== * ローカルプロトタイプ宣言 (必要な物のみ) @@ -374,26 +387,66 @@ const char* script_op2name(int op) #undef RETURN_OP_NAME } -//Reports on the console the src of a script error. -static void report_src(struct script_state *st) +/// Reports on the console the src of a script error. +static void script_reportsrc(struct script_state *st) { - struct block_list *bl; - if (!st->oid) return; //Can't report source. + struct block_list* bl; + + if( st->oid == 0 ) + return; //Can't report source. + bl = map_id2bl(st->oid); - if (!bl) return; - switch (bl->type) { - case BL_NPC: - if (bl->m >=0) - ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); - else - ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); - + if( bl == NULL ) + return; + + switch( bl->type ) + { + case BL_NPC: + if( bl->m >= 0 ) + ShowDebug("Source (NPC): %s at %s (%d,%d)\n", ((struct npc_data *)bl)->name, map[bl->m].name, bl->x, bl->y); + else + ShowDebug("Source (NPC): %s (invisible/not on a map)\n", ((struct npc_data *)bl)->name); break; - default: - if (bl->m >=0) - ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y); + default: + if( bl->m >= 0 ) + ShowDebug("Source (Non-NPC type %d): name %s at %s (%d,%d)\n", bl->type, status_get_name(bl), map[bl->m].name, bl->x, bl->y); + else + ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl)); + break; + } +} + +/// Reports on the console information about the script data. +static void script_reportdata(struct script_data* data) +{ + if( data == NULL ) + return; + switch( data->type ) + { + case C_NOP:// no value + ShowDebug("Data: no value (nil)\n"); + break; + case C_INT:// number + ShowDebug("Data: number=%d\n", data->u.num); + break; + case C_STR: + case C_CONSTSTR:// string + ShowDebug("Data: string=%s\n", data->u.str); + break; + case C_NAME:// variable + { + char* name = data_varname(data); + if( not_array_variable(*name) ) + ShowDebug("Data: variable=%s\n", name); else - ShowDebug("Source (Non-NPC type %d): name %s (invisible/not on a map)\n", bl->type, status_get_name(bl)); + ShowDebug("Data: variable=%s index=%u\n", name, data_varindex(data)); + } + break; + case C_POS:// label + ShowDebug("Data: label pos=%d\n", data->u.num); + break; + default: + ShowDebug("Data: %s\n", script_op2name(data->type)); break; } } @@ -415,7 +468,7 @@ static void check_event(struct script_state *st, const char *evt) { if( evt != NULL && *evt != '\0' && !stristr(evt,"::On") ){ ShowError("NPC event parameter deprecated! Please use 'NPCNAME::OnEVENT' instead of '%s'.\n",evt); - report_src(st); + script_reportsrc(st); } } @@ -440,8 +493,9 @@ static unsigned int calc_hash2(const char* p) unsigned int g; while( *p ){ // UNIX ELF hash h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++)); - if ( g = h & 0xF0000000 ) - h ^= g >> 24; + g = h & 0xF0000000; + if( g ) + h ^= g >> 24; h &= ~g; } return h; @@ -450,7 +504,9 @@ static unsigned int calc_hash2(const char* p) unsigned int g; while( *p ){ h = ( h << 4 ) + ((unsigned char)TOLOWER(*p++)); - if ( (g=h&0xF0000000) ) { + g = h & 0xF0000000; + if( g ) + { h ^= g>>24; h ^= g; } @@ -460,7 +516,7 @@ static unsigned int calc_hash2(const char* p) unsigned int h = 0; while( *p ){ h = ( h << 1 ) + ( h >> 3 ) + ( h >> 5 ) + ( h >> 8 ); - h+=(unsigned char)TOLOWER(*p++); + h += (unsigned char)TOLOWER(*p++); } return h; #endif @@ -2447,13 +2503,14 @@ void op_3(struct script_state* st, int op) get_val(st, data); if( data_isstring(data) ) - flag = data->u.str[0]; + flag = data->u.str[0];// "" -> false else if( data_isint(data) ) - flag = data->u.num; + flag = data->u.num;// 0 -> false else { - ShowError("script:op_3: invalid type of data op:%s data:%s\n", script_op2name(op), script_op2name(data->type)); - report_src(st); + ShowError("script:op_3: invalid data for the ternary operator test\n"); + script_reportdata(data); + script_reportsrc(st); script_removetop(st, -3, 0); script_pushnil(st); return; @@ -2493,8 +2550,8 @@ void op_2str(struct script_state* st, int op, const char* s1, const char* s2) return; } default: - ShowError("script:op2_str: unexpected string operator op:%s\n", script_op2name(op)); - report_src(st); + ShowError("script:op2_str: unexpected string operator %s\n", script_op2name(op)); + script_reportsrc(st); script_pushnil(st); st->state = END; return; @@ -2529,8 +2586,8 @@ void op_2num(struct script_state* st, int op, int i1, int i2) case C_MOD: if( i2 == 0 ) { - ShowError("script:op_2num: division by zero detected op:%s\n", script_op2name(op)); - report_src(st); + ShowError("script:op_2num: division by zero detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); + script_reportsrc(st); script_pushnil(st); st->state = END; return; @@ -2547,21 +2604,21 @@ void op_2num(struct script_state* st, int op, int i1, int i2) case C_SUB: ret = i1 - i2; ret_double = (double)i1 - (double)i2; break; case C_MUL: ret = i1 * i2; ret_double = (double)i1 * (double)i2; break; default: - ShowError("script:op_2num: unexpected number operator op:%s\n", script_op2name(op)); - report_src(st); + ShowError("script:op_2num: unexpected number operator %s i1=%d i2=%d\n", script_op2name(op), i1, i2); + script_reportsrc(st); script_pushnil(st); return; } - if( ret_double < INT_MIN ) + if( ret_double < (double)INT_MIN ) { - ShowWarning("script:op_2num: underflow detected op:%s\n", script_op2name(op)); - report_src(st); + ShowWarning("script:op_2num: underflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); + script_reportsrc(st); ret = INT_MIN; } - else if( ret_double > INT_MAX ) + else if( ret_double > (double)INT_MAX ) { - ShowWarning("script:op_2num: overflow detected op:%s\n", script_op2name(op)); - report_src(st); + ShowWarning("script:op_2num: overflow detected op=%s i1=%d i2=%d\n", script_op2name(op), i1, i2); + script_reportsrc(st); ret = INT_MAX; } } @@ -2584,9 +2641,12 @@ void op_2(struct script_state *st, int op) switch( op ) { case C_ADD: - if( data_isstring(left) || data_isstring(right) ) - {// convert to string + if( data_isint(left) && data_isstring(right) ) + {// convert int-string to string-string conv_str(st, left); + } + else if( data_isstring(left) && data_isint(right) ) + {// convert string-int to string-string conv_str(st, right); } break; @@ -2606,8 +2666,10 @@ void op_2(struct script_state *st, int op) } else {// invalid argument - ShowError("script:op_2: invalid type of data op:%s left:%s right:%s\n", script_op2name(op), script_op2name(left->type), script_op2name(right->type)); - report_src(st); + ShowError("script:op_2: invalid data for operator %s\n", script_op2name(op)); + script_reportdata(left); + script_reportdata(right); + script_reportsrc(st); script_removetop(st, -2, 0); script_pushnil(st); st->state = END; @@ -2628,8 +2690,9 @@ void op_1(struct script_state* st, int op) if( !data_isint(data) ) {// not a number - ShowError("script:op_1: invalid type of data op:%s data:%s\n", script_op2name(op), script_op2name(data->type)); - report_src(st); + ShowError("script:op_1: argument is not a number (op=%s)\n", script_op2name(op)); + script_reportdata(data); + script_reportsrc(st); script_pushnil(st); st->state = END; return; @@ -2643,8 +2706,8 @@ void op_1(struct script_state* st, int op) case C_NOT: i1 = ~i1; break; case C_LNOT: i1 = !i1; break; default: - ShowError("script:op_1: unexpected operator op:%s\n", script_op2name(op)); - report_src(st); + ShowError("script:op_1: unexpected operator %s i1=%d\n", script_op2name(op), i1); + script_reportsrc(st); script_pushnil(st); st->state = END; return; @@ -2667,7 +2730,7 @@ int run_func(struct script_state *st) ShowError("function not found\n"); // st->stack->sp=0; st->state=END; - report_src(st); + script_reportsrc(st); return 1; } start_sp=i-1; @@ -2711,7 +2774,7 @@ int run_func(struct script_state *st) ShowMessage ("run_func: '"CL_WHITE"%s"CL_RESET"' (type %d) is not function and command!\n", str_buf+str_data[func].str, str_data[func].type); // st->stack->sp=0; st->state=END; - report_src(st); + script_reportsrc(st); return 1; } #ifdef DEBUG_RUN @@ -2721,12 +2784,12 @@ int run_func(struct script_state *st) #endif if(str_data[func].func){ if (str_data[func].func(st)) //Report error - report_src(st); + script_reportsrc(st); } else { if(battle_config.error_log) ShowError("run_func : %s? (%d(%d))\n",str_buf+str_data[func].str,func,str_data[func].type); script_pushint(st,0); - report_src(st); + script_reportsrc(st); } // Stack's datum are used when re-run functions [Eoe] @@ -2743,7 +2806,7 @@ int run_func(struct script_state *st) if(st->stack->defsp<5 || st->stack->stack_data[st->stack->defsp-1].type!=C_RETINFO){ ShowWarning("script:run_func(return) return without callfunc or callsub!\n"); st->state=END; - report_src(st); + script_reportsrc(st); return 1; } script_free_vars( st->stack->var_function ); @@ -2795,7 +2858,7 @@ void run_script(struct script_code *rootscript,int pos,int rid,int oid) st->pos = pos; st->rid = rid; st->oid = oid; - st->sleep.timer = -1; + st->sleep.timer = INVALID_TIMER; run_script_main(st); } @@ -2931,7 +2994,7 @@ void run_script_main(struct script_state *st) st->state = RUN; if( gotocount>0 && (--gotocount)<=0 ){ ShowError("run_script: infinity loop !\n"); - report_src(st); + script_reportsrc(st); st->state=END; } } @@ -2980,7 +3043,7 @@ void run_script_main(struct script_state *st) } if( cmdcount>0 && (--cmdcount)<=0 ){ ShowError("run_script: infinity loop !\n"); - report_src(st); + script_reportsrc(st); st->state=END; } } @@ -3004,9 +3067,9 @@ void run_script_main(struct script_state *st) ShowWarning("Unable to restore stack! Double continuation!\n"); //Report BOTH scripts to see if that can help somehow. ShowDebug("Previous script (lost):\n"); - report_src(bk_st); + script_reportsrc(bk_st); ShowDebug("Current script:\n"); - report_src(st); + script_reportsrc(st); } } else { //Dispose of script. @@ -3443,7 +3506,7 @@ static int do_final_userfunc_sub (DBKey key,void *data,va_list ap) *------------------------------------------*/ int do_final_script() { -#ifdef DEBUG_RUN +#ifdef DEBUG_HASH if (battle_config.etc_log) { FILE *fp = fopen("hash_dump.txt","wt"); @@ -3931,12 +3994,12 @@ struct script_function buildin_func[] = { BUILDIN_DEF(warpguild,"siii"), // [Fredzilla] BUILDIN_DEF(setlook,"ii"), BUILDIN_DEF(set,"ii"), - BUILDIN_DEF(setarray,"ii*"), - BUILDIN_DEF(cleararray,"iii"), - BUILDIN_DEF(copyarray,"iii"), - BUILDIN_DEF(getarraysize,"i"), - BUILDIN_DEF(deletearray,"ii"), - BUILDIN_DEF(getelementofarray,"ii"), + BUILDIN_DEF(setarray,"rv*"), + BUILDIN_DEF(cleararray,"rvi"), + BUILDIN_DEF(copyarray,"rri"), + BUILDIN_DEF(getarraysize,"r"), + BUILDIN_DEF(deletearray,"r?"), + BUILDIN_DEF(getelementofarray,"ri"), BUILDIN_DEF(getitem,"vi?"), BUILDIN_DEF(getitem2,"iiiiiiiii*"), BUILDIN_DEF(getnameditem,"is"), @@ -4374,7 +4437,7 @@ BUILDIN_FUNC(menu) // TODO detect multiple scripts waiting for input at the same time, and what to do when that happens if( sd->state.menu_or_input == 0 ) { - struct StringBuf* buf; + struct StringBuf buf; struct script_data* data; if( script_lastdata(st) % 2 == 0 ) @@ -4383,7 +4446,7 @@ BUILDIN_FUNC(menu) st->state = END; return 1; } - buf = StringBuf_Malloc(); + StringBuf_Init(&buf); for( i = 2, sd->npc_menu = 0; i < script_lastdata(st); i += 2 ) { // menu options @@ -4391,8 +4454,9 @@ BUILDIN_FUNC(menu) get_val(st, data); if( data_isstring(data) && data_isint(data) ) {// not a string (or compatible) - StringBuf_Free(buf); - ShowError("script:menu: argument #%d (from 1) is not a string or compatible (op=%s).\n", (i - 1), script_op2name(data->type)); + StringBuf_Destroy(&buf); + ShowError("script:menu: argument #%d (from 1) is not a string or compatible.\n", (i - 1)); + script_reportdata(data); st->state = END; return 1; } @@ -4402,8 +4466,9 @@ BUILDIN_FUNC(menu) data = script_getdata(st, i+1); if( !data_islabel(data) ) {// not a label - StringBuf_Free(buf); - ShowError("script:menu: argument #%d (from 1) is not a label or label not found (op=%s).\n", i, script_op2name(data->type)); + StringBuf_Destroy(&buf); + ShowError("script:menu: argument #%d (from 1) is not a label or label not found.\n", i); + script_reportdata(data); st->state = END; return 1; } @@ -4412,14 +4477,14 @@ BUILDIN_FUNC(menu) if( text[0] == '\0' ) continue;// empty string, ignore if( sd->npc_menu > 0 ) - StringBuf_AppendStr(buf, ":"); - StringBuf_AppendStr(buf, text); + StringBuf_AppendStr(&buf, ":"); + StringBuf_AppendStr(&buf, text); sd->npc_menu += menu_countoptions(text, 0, NULL); } st->state = RERUNLINE; sd->state.menu_or_input = 1; - clif_scriptmenu(sd, st->oid, StringBuf_Value(buf)); - StringBuf_Free(buf); + clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); + StringBuf_Destroy(&buf); //TODO what's the maximum number of options that can be displayed and/or received? -> give warning } else if( sd->npc_menu == 0xff ) @@ -4449,14 +4514,16 @@ BUILDIN_FUNC(menu) } if( sd->npc_menu > 0 ) {// Invalid selection - ShowDebug("script:menu: selection is out of range, expected %d extra menu options\n", sd->npc_menu); + ShowDebug("script:menu: selection is out of range (%d pairs are missing?) - please report this\n", sd->npc_menu); st->state = END; return 1; } if( !data_islabel(script_getdata(st, i + 1)) ) {// TODO remove this temporary crash-prevention code (fallback for multiple scripts requesting user input) + ShowError("script:menu: unexpected data in label argument\n"); + script_reportdata(script_getdata(st, i + 1)); st->state = END; - return 0; + return 1; } pc_setreg(sd, add_str("@menu"), menu); st->pos = script_getnum(st, i + 1); @@ -4483,22 +4550,22 @@ BUILDIN_FUNC(select) if( sd->state.menu_or_input == 0 ) { - struct StringBuf* buf; + struct StringBuf buf; - buf = StringBuf_Malloc(); + StringBuf_Init(&buf); for( i = 2, sd->npc_menu = 0; i <= script_lastdata(st); ++i ) { text = script_getstr(st, i); if( sd->npc_menu > 0 ) - StringBuf_AppendStr(buf, ":"); - StringBuf_AppendStr(buf, script_getstr(st, i)); + StringBuf_AppendStr(&buf, ":"); + StringBuf_AppendStr(&buf, text); sd ->npc_menu += menu_countoptions(text, 0, NULL); } st->state = RERUNLINE; sd->state.menu_or_input = 1; - clif_scriptmenu(sd, st->oid, StringBuf_Value(buf)); - StringBuf_Free(buf); + clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); + StringBuf_Destroy(&buf); } else if( sd->npc_menu == 0xff ) {// Cancel was pressed @@ -4543,22 +4610,22 @@ BUILDIN_FUNC(prompt) if( sd->state.menu_or_input == 0 ) { - struct StringBuf* buf; + struct StringBuf buf; - buf = StringBuf_Malloc(); + StringBuf_Init(&buf); for( i = 2, sd->npc_menu = 0; i <= script_lastdata(st); ++i ) { text = script_getstr(st, i); if( sd->npc_menu > 0 ) - StringBuf_AppendStr(buf, ":"); - StringBuf_AppendStr(buf, script_getstr(st, i)); + StringBuf_AppendStr(&buf, ":"); + StringBuf_AppendStr(&buf, text); sd ->npc_menu += menu_countoptions(text, 0, NULL); } st->state = RERUNLINE; sd->state.menu_or_input = 1; - clif_scriptmenu(sd, st->oid, StringBuf_Value(buf)); - StringBuf_Free(buf); + clif_scriptmenu(sd, st->oid, StringBuf_Value(&buf)); + StringBuf_Destroy(&buf); } else if( sd->npc_menu == 0xff ) {// Cancel was pressed @@ -4595,7 +4662,8 @@ BUILDIN_FUNC(goto) { if( !data_islabel(script_getdata(st,2)) ) { - ShowError("script:goto: not label!\n"); + ShowError("script:goto: not a label\n"); + script_reportdata(script_getdata(st,2)); st->state = END; return 1; } @@ -4646,7 +4714,7 @@ BUILDIN_FUNC(callfunc) } } }else{ - ShowWarning("script:callfunc: function not found! [%s]\n",str); + ShowError("script:callfunc: function not found! [%s]\n",str); st->state=END; return 1; } @@ -4660,7 +4728,8 @@ BUILDIN_FUNC(callsub) int pos=script_getnum(st,2); int i,j; if(!data_islabel(script_getdata(st,2)) && !data_isfunclabel(script_getdata(st,2))) { - ShowError("script: callsub: not label !\n"); + ShowError("script:callsub: argument is not a label\n"); + script_reportdata(script_getdata(st,2)); st->state=END; return 1; } else { @@ -4707,7 +4776,7 @@ BUILDIN_FUNC(getarg) if( st->stack->defsp < 5 || st->stack->stack_data[st->stack->defsp - 1].type != C_RETINFO ) { - ShowWarning("script:getarg: no callfunc or callsub!\n"); + ShowError("script:getarg: no callfunc or callsub!\n"); st->state = END; return 1; } @@ -4722,7 +4791,7 @@ BUILDIN_FUNC(getarg) script_pushcopy(st, 3); else { - ShowWarning("script:getarg: index (idx=%d) out of range (count=%d) and no default value found\n", idx, count); + ShowError("script:getarg: index (idx=%d) out of range (count=%d) and no default value found\n", idx, count); st->state = END; return 1; } @@ -4744,7 +4813,7 @@ BUILDIN_FUNC(return) data = script_getdatatop(st, -1); if( data_isreference(data) ) { - char* name = data_referencename(data); + char* name = data_varname(data); if( name[0] == '.' && name[1] == '@' ) {// temporary script variable, convert to value get_val(st, data); @@ -5185,7 +5254,8 @@ BUILDIN_FUNC(input) if (!sd) return 1; if( !data_isreference(data) ){ - ShowError("script: buildin_input: given argument is not a variable!\n"); + ShowError("script:input: not a variable\n"); + script_reportdata(data); return 1; } @@ -5227,7 +5297,9 @@ BUILDIN_FUNC(set) char postfix=name[strlen(name)-1]; if( !data_isreference(script_getdata(st,2)) ){ - ShowError("script: buildin_set: not name\n"); + ShowError("script:set: not a variable\n"); + script_reportdata(script_getdata(st,2)); + st->state = END; return 1; } @@ -5247,217 +5319,403 @@ BUILDIN_FUNC(set) return 0; } -/*========================================== - * 配列変数設定 - *------------------------------------------*/ +///////////////////////////////////////////////////////////////////// +/// Array variables +/// + +/// Returns the size of the specified array +static int32 getarraysize(struct script_state* st, int32 id, int32 idx, int isstring, struct linkdb_node** ref) +{ + int32 ret = idx; + + if( isstring ) + { + for( ; idx < 128; ++idx ) + { + char* str = (char*)get_val2(st, variable_uid(id, idx), ref); + if( str && *str ) + ret = idx + 1; + } + } + else + { + for( ; idx < 128; ++idx ) + { + int32 num = (int32)get_val2(st, variable_uid(id, idx), ref); + if( num ) + ret = idx + 1; + } + } + return ret; +} + +/// Sets values of an array, from the starting index. +/// ex: setarray arr[1],1,2,3; +/// +/// setarray <array variable>,<value1>{,<value2>...}; BUILDIN_FUNC(setarray) { - TBL_PC *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int i,j; + struct script_data* data; + char* name; + int32 start; + int32 end; + int32 id; + int32 i; + TBL_PC* sd = NULL; - if( prefix!='$' && prefix!='@' && prefix!='.'){ - ShowWarning("buildin_setarray: illegal scope !\n"); - return 1; + data = script_getdata(st, 2); + if( !data_isreference(data) ) + { + ShowError("script:setarray: not a variable\n"); + script_reportdata(data); + st->state = END; + return 1;// not a variable } - if(not_server_variable(prefix)) - sd=script_rid2sd(st); - for(j=0,i=st->start+3; i<st->end && j<128;i++,j++){ - void *v; - if( postfix=='$' ) - v=(void*)conv_str(st,& (st->stack->stack_data[i])); - else - v=(void*)conv_num(st,& (st->stack->stack_data[i])); - set_reg(st, sd, num+(j<<24), name, v, script_getref(st,2)); + id = data_varid(data); + start = data_varindex(data); + name = data_varname(data); + if( not_array_variable(*name) ) + { + ShowError("script:setarray: illegal scope\n"); + script_reportdata(data); + st->state = END; + return 1;// not supported + } + + if( not_server_variable(*name) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 1;// no player attached + } + + end = start + script_lastdata(st) - 2; + if( end > 127 ) + end = 127; + + if( is_string_variable(name) ) + {// string array + for( i = 3; start < end; ++start, ++i ) + { + void* v = (void*)script_getstr(st,i); + set_reg(st, sd, variable_uid(id, start), name, v, data_varref(data)); + } + } + else + {// int array + for( i = 3; start < end; ++start, ++i ) + { + void* v = (void*)script_getnum(st,i); + set_reg(st, sd, variable_uid(id, start), name, v, data_varref(data)); + } } return 0; } -/*========================================== - * 配列変数クリア - *------------------------------------------*/ + +/// Sets count values of an array, from the starting index. +/// ex: cleararray arr[0],0,1; +/// +/// cleararray <array variable>,<value>,<count>; BUILDIN_FUNC(cleararray) { - TBL_PC *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int sz=script_getnum(st,4); - int i; - void *v; + struct script_data* data; + char* name; + int32 start; + int32 end; + int32 id; + void* v; + TBL_PC* sd = NULL; - if( prefix!='$' && prefix!='@' && prefix!='.'){ - ShowWarning("buildin_cleararray: illegal scope !\n"); - return 1; + data = script_getdata(st, 2); + if( !data_isreference(data) ) + { + ShowError("script:cleararray: not a variable\n"); + script_reportdata(data); + st->state = END; + return 1;// not a variable } - if( not_server_variable(prefix) ) - sd=script_rid2sd(st); - if( postfix=='$' ) - v=(void*)script_getstr(st,3); + id = data_varid(data); + start = data_varindex(data); + name = data_varname(data); + if( not_array_variable(*name) ) + { + ShowError("script:cleararray: illegal scope\n"); + script_reportdata(data); + st->state = END; + return 1;// not supported + } + + if( not_server_variable(*name) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 1;// no player attached + } + + if( is_string_variable(name) ) + v = (void*)script_getstr(st, 3); else - v=(void*)script_getnum(st,3); + v = (void*)script_getnum(st, 3); - for(i=0;i<sz;i++) - set_reg(st,sd,num+(i<<24),name,v,script_getref(st,2)); + end = start + script_getnum(st, 4); + if( end > 127 ) + end = 127; + + for( ; start < end; ++start ) + set_reg(st, sd, variable_uid(id, start), name, v, script_getref(st,2)); return 0; } -/*========================================== - * 配列変数コピー - *------------------------------------------*/ + +/// Copies data from one array to another. +/// ex: copyarray arr[0],arr[2],2; +/// +/// copyarray <destination array variable>,<source array variable>,<count>; BUILDIN_FUNC(copyarray) { - TBL_PC *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int num2=st->stack->stack_data[st->start+3].u.num; - char *name2=str_buf+str_data[num2&0x00ffffff].str; - char prefix2=*name2; - char postfix2=name2[strlen(name2)-1]; - int sz=script_getnum(st,4); - int i; + struct script_data* data1; + struct script_data* data2; + char* name1; + char* name2; + int32 idx1; + int32 idx2; + int32 id1; + int32 id2; + void* v; + int32 i; + int32 count; + TBL_PC* sd = NULL; - if( prefix!='$' && prefix!='@' && prefix!='.' ){ - printf("buildin_copyarray: illeagal scope !\n"); - return 0; + data1 = script_getdata(st, 2); + data2 = script_getdata(st, 3); + if( !data_isreference(data1) || !data_isreference(data2) ) + { + ShowError("script:copyarray: not a variable\n"); + script_reportdata(data1); + script_reportdata(data2); + st->state = END; + return 1;// not a variable } - if( prefix2!='$' && prefix2!='@' && prefix2!='.' ) { - printf("buildin_copyarray: illeagal scope !\n"); - return 0; + + id1 = data_varid(data1); + id2 = data_varid(data2); + idx1 = data_varindex(data1); + idx2 = data_varindex(data2); + name1 = data_varname(data1); + name2 = data_varname(data2); + if( not_array_variable(*name1) || not_array_variable(*name2) ) + { + ShowError("script:copyarray: illegal scope\n"); + script_reportdata(data1); + script_reportdata(data2); + st->state = END; + return 1;// not supported } - if( (postfix=='$' || postfix2=='$') && postfix!=postfix2 ){ - printf("buildin_copyarray: type mismatch !\n"); - return 0; + + if( is_string_variable(name1) != is_string_variable(name2) ) + { + ShowError("script:copyarray: type mismatch\n"); + script_reportdata(data1); + script_reportdata(data2); + st->state = END; + return 1;// data type mismatch } - if( not_server_variable(prefix) || not_server_variable(prefix2) ) - sd=script_rid2sd(st); - if((num & 0x00FFFFFF) == (num2 & 0x00FFFFFF) && (num & 0xFF000000) > (num2 & 0xFF000000)) { - // 同じ配列で、num > num2 の場合大きい方からコピーしないといけない - for(i=sz-1;i>=0;i--) - set_reg( - st,sd,num+(i<<24),name, - get_val2(st,num2+(i<<24),script_getref(st,3)), - script_getref(st,2) - ); - } else { - for(i=0;i<sz;i++) - set_reg( - st,sd,num+(i<<24),name, - get_val2(st,num2+(i<<24),script_getref(st,3)), - script_getref(st,2) - ); + if( not_server_variable(*name1) || not_server_variable(*name2) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 1;// no player attached } - return 0; -} -/*========================================== - * Returns the size of the specified array - *------------------------------------------*/ -static int getarraysize(struct script_state* st, int num, int postfix, struct linkdb_node** ref) -{ - //TODO: unreadable; remove +-1 trick, remove weird for loop, remove typecast - int i = (num>>24), c = (i==0?-1:i); // Moded to -1 because even if the first element is 0, it will still report as 1 [Lance] - - if(postfix == '$') { - for(; i < 128; i++) { - void* v = get_val2(st, (num & 0x00FFFFFF) + (i<<24), ref); - if(*((char*)v)) c = i; + count = script_getnum(st, 4); + if( count > 128 - idx1 ) + count = 128 - idx1; + if( count <= 0 || (id1 == id2 && idx1 == idx2) ) + return 0;// nothing to copy + + if( id1 == id2 && idx1 > idx2 ) + {// destination might be overlapping the source - copy in reverse order + for( i = count - 1; i >= 0; --i ) + { + v = get_val2(st, variable_uid(id2, idx2 + i), data_varref(data2)); + set_reg(st, sd, variable_uid(id1, idx1 + i), name1, v, data_varref(data1)); } - } else { - for(; i < 128; i++) { - void* v = get_val2(st, (num & 0x00FFFFFF) + (i<<24), ref); - if((int)v) c = i; + } + else + {// normal copy + for( i = 0; i < count; ++i ) + { + if( id2 + i < 128 ) + v = get_val2(st, variable_uid(id2, idx2 + i), data_varref(data2)); + else// out of range - assume ""/0 + v = (void*)(is_string_variable(name1) ? "" : 0); + set_reg(st, sd, variable_uid(id1, idx1 + i), name1, v, data_varref(data1)); } } - return c + 1; + return 0; } +/// Returns the size of the array. +/// Assumes that everything before the starting index exists. +/// ex: getarraysize(arr[3]) +/// +/// getarraysize(<array variable>) -> <int> BUILDIN_FUNC(getarraysize) { - int num = st->stack->stack_data[st->start+2].u.num; - char* name = str_buf + str_data[num&0x00ffffff].str; - char prefix = name[0], postfix = name[strlen(name)-1]; + struct script_data* data; + char* name; - if( prefix != '$' && prefix != '@' && prefix != '.' ) { - ShowWarning("buildin_getarraysize: illegal scope !\n"); - script_pushint(st,0); - return 1; + data = script_getdata(st, 2); + if( !data_isreference(data) ) + { + ShowError("script:getarraysize: not a variable\n"); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1;// not a variable + } + + name = data_varname(data); + if( not_array_variable(*name) ) + { + ShowError("script:getarraysize: illegal scope\n"); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1;// not supported } - script_pushint(st,getarraysize(st, num, postfix, script_getref(st,2))); + script_pushint(st, getarraysize(st, data_varid(data), data_varindex(data), is_string_variable(name), data_varref(data))); return 0; } -/*========================================== - * 配列変数から要素削除 - *------------------------------------------*/ + +/// Deletes count or all the elements in an array, from the starting index. +/// ex: deletearray arr[4],2; +/// +/// deletearray <array variable>; +/// deletearray <array variable>,<count>; BUILDIN_FUNC(deletearray) { - TBL_PC *sd=NULL; - int num=st->stack->stack_data[st->start+2].u.num; - char *name=str_buf+str_data[num&0x00ffffff].str; - char prefix=*name; - char postfix=name[strlen(name)-1]; - int count=1; - int i,sz=getarraysize(st,num,postfix,script_getref(st,2))-(num>>24)-count+1; + struct script_data* data; + char* name; + int start; + int end; + int id; + TBL_PC *sd = NULL; + data = script_getdata(st, 2); + if( !data_isreference(data) ) + { + ShowError("script:deletearray: not a variable\n"); + script_reportdata(data); + st->state = END; + return 1;// not a variable + } - if( script_hasdata(st,3) ) - count=script_getnum(st,3); + id = data_varid(data); + start = data_varindex(data); + name = data_varname(data); + if( not_array_variable(*name) ) + { + ShowError("script:deletearray: illegal scope\n"); + script_reportdata(data); + st->state = END; + return 1;// not supported + } - if( prefix!='$' && prefix!='@' && prefix!='.' ){ - ShowWarning("buildin_deletearray: illegal scope !\n"); - return 1; + if( not_server_variable(*name) ) + { + sd = script_rid2sd(st); + if( sd == NULL ) + return 1;// no player attached } - if( not_server_variable(prefix) ) - sd=script_rid2sd(st); - for(i=0;i<sz;i++){ - set_reg( - st,sd,num+(i<<24),name, - get_val2(st,num+((i+count)<<24),script_getref(st,2)), - script_getref(st,2) - ); + end = getarraysize(st, id, start, is_string_variable(name), data_varref(data)); + if( start >= end ) + return 0;// nothing to free + + if( script_hasdata(st,3) ) + { + int count = script_getnum(st, 3); + if( count > end - start ) + count = end - start; + if( count <= 0 ) + return 0;// nothing to free + + // move rest of the elements backward + for( ; start + count < end; ++start ) + { + void* v = get_val2(st, variable_uid(id, start + count), data_varref(data)); + set_reg(st, sd, variable_uid(id, start), name, v, data_varref(data)); + } } - if(postfix != '$'){ - for(;i<(128-(num>>24));i++) - set_reg(st,sd,num+(i<<24),name, 0,script_getref(st,2)); - } else { - for(;i<(128-(num>>24));i++) - set_reg(st,sd,num+(i<<24),name, (void *) "",script_getref(st,2)); + // clear the rest of the array + if( is_string_variable(name) ) + { + for( ; start < end; ++start ) + set_reg(st, sd, variable_uid(id, start), name, (void*)0, data_varref(data)); + } + else + { + for( ; start < end; ++start ) + set_reg(st, sd, variable_uid(id, start), name, (void *)"", data_varref(data)); } return 0; } -/*========================================== - * 指定要素を表す値(キー)を所得する - *------------------------------------------*/ +/// Returns a reference to the target index of the array variable. +/// Equivalent to var[index]. +/// +/// getelementofarray(<array variable>,<index>) -> <variable reference> BUILDIN_FUNC(getelementofarray) { - if( data_isreference(script_getdata(st, 2)) ){ - int i=script_getnum(st,3); - if(i>127 || i<0){ - ShowWarning("script: getelementofarray (operator[]): param2 illegal number %d\n",i); - script_pushint(st,0); - return 1; - }else{ - push_val2(st->stack,C_NAME, - (i<<24) | st->stack->stack_data[st->start+2].u.num, script_getref(st,2) ); - } - }else{ - ShowError("script: getelementofarray (operator[]): param1 not name !\n"); - script_pushint(st,0); + struct script_data* data; + char* name; + int32 id; + int i; + + data = script_getdata(st, 2); + if( !data_isreference(data) ) + { + ShowError("script:getelementofarray: not a variable\n"); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1;// not a variable + } + + id = data_varid(data); + name = data_varname(data); + if( not_array_variable(*name) ) + { + ShowError("script:getelementofarray: illegal scope\n"); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1;// not supported + } + + i = script_getnum(st, 3); + if( i < 0 || i >= 128 ) + { + ShowWarning("script:getelementofarray: index out of range (%d)\n", i); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1;// out of range } + + push_val2(st->stack, C_NAME, variable_uid(id, i), data_varref(data)); return 0; } +///////////////////////////////////////////////////////////////////// +/// ... +/// + /*========================================== * *------------------------------------------*/ @@ -5664,7 +5922,6 @@ BUILDIN_FUNC(getitem) struct item_data *item_data = itemdb_searchname(name); if( item_data == NULL ){ ShowError("buildin_getitem: Nonexistant item %s requested.\n", name); - report_src(st); return 1; //No item created. } nameid=item_data->nameid; @@ -5678,12 +5935,10 @@ BUILDIN_FUNC(getitem) } if( nameid <= 0 || !itemdb_exists(nameid) ){ ShowError("buildin_getitem: Nonexistant item %d requested.\n", nameid); - report_src(st); return 1; //No item created. } } else { ShowError("buildin_getitem: invalid data type for argument #1 (%d).", data->type); - report_src(st); return 1; } @@ -6831,7 +7086,7 @@ BUILDIN_FUNC(bonus) pc_bonus5(sd, type, type2, type3, type4, type5, val); break; default: - ShowDebug("buildin_bonus: unexpected last data (%d)\n", script_lastdata(st)); + ShowDebug("buildin_bonus: unexpected number of arguments (%d)\n", (script_lastdata(st) - 2)); } return 0; @@ -10074,8 +10329,8 @@ BUILDIN_FUNC(guardian) {// <guardian index> guardian=script_getnum(st,7); } else { - ShowError("buildin_guardian: invalid data type for argument #8 (%d).", data->type); - report_src(st); + ShowError("script:guardian: invalid data type for argument #6 (from 1)\n"); + script_reportdata(data); return 1; } } @@ -13049,7 +13304,7 @@ BUILDIN_FUNC(unitattack) default: ShowError("script:unitattack: unsupported source unit type %d\n", unit_bl->type); script_pushint(st, 0); - return 0; + return 1; } script_pushint(st, unit_walktobl(unit_bl, target_bl, 65025, 2)); return 0; @@ -13152,7 +13407,7 @@ BUILDIN_FUNC(unitdeadsit) } else { ShowWarning("script:unitdeadsit: %d is not a valid action\n", action); - report_src(st); + return 1; } } @@ -13320,34 +13575,39 @@ BUILDIN_FUNC(awake) BUILDIN_FUNC(getvariableofnpc) { struct script_data* data; + char* name; + struct npc_data* nd; data = script_getdata(st,2); if( !data_isreference(data) ) {// Not a reference (aka varaible name) - ShowError("script: getvariableofnpc: first argument is not a variable name\n"); + ShowError("script:getvariableofnpc: not a variable\n"); + script_reportdata(data); + script_pushnil(st); st->state = END; return 1; } - else - { - int num = data->u.num; - char* var_name = str_buf + str_data[num&0x00ffffff].str; - const char* npc_name = script_getstr(st, 3); - struct npc_data* nd = npc_name2id(npc_name); - if( var_name[0] != '.' || var_name[1] == '@' ) - {// not a npc variable - ShowError("script: getvariableofnpc: invalid scope %s (not npc variable)\n", var_name); - st->state = END; - return 1; - } - if( nd == NULL || nd->bl.subtype != SCRIPT || nd->u.scr.script == NULL ) - {// NPC not found or has no script - ShowError("script: getvariableofnpc: can't find npc %s\n", npc_name); - st->state = END; - return 1; - } - push_val2(st->stack, C_NAME, num, &nd->u.scr.script->script_vars ); + + name = data_varname(data); + if( *name != '.' || name[1] == '@' ) + {// not a npc variable + ShowError("script:getvariableofnpc: invalid scope (not npc variable)\n"); + script_reportdata(data); + script_pushnil(st); + st->state = END; + return 1; + } + + nd = npc_name2id(script_getstr(st,3)); + if( nd == NULL || nd->bl.subtype != SCRIPT || nd->u.scr.script == NULL ) + {// NPC not found or has no script + ShowError("script:getvariableofnpc: can't find npc %s\n", script_getstr(st,3)); + script_pushnil(st); + st->state = END; + return 1; } + + push_val2(st->stack, C_NAME, data_varuid(data), &nd->u.scr.script->script_vars ); return 0; } @@ -13370,7 +13630,7 @@ BUILDIN_FUNC(warpportal) bl = map_id2bl(st->oid); if( bl == NULL ) { - ShowError("script:warpportal: npc is needed"); + ShowError("script:warpportal: npc is needed\n"); return 1; } diff --git a/src/map/script.h b/src/map/script.h index 4152999d6..b207bd36e 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -44,8 +44,10 @@ struct script_code { }; struct script_stack { - int sp,sp_max,defsp; - struct script_data *stack_data; + int sp;// number of entries in the stack + int sp_max;// capacity of the stack + int defsp; + struct script_data *stack_data;// stack struct linkdb_node **var_function; // 関数依存変数 }; |