浏览代码

* Reworked the parsing at npc.c.
- Fixes npc.c discarding the '}' at the end of file, when there is no newline. (uncovered as a side-effect of r11487)
* Empty script functions always have code now (won't report as missing when you try to call them).
* Changed userfunc_db to not limit the name to 50 characters.

git-svn-id: https://svn.code.sf.net/p/rathena/svn/trunk@11502 54d463be-8e91-2dee-dedb-b68131a5f0ec

FlavioJS 17 年之前
父节点
当前提交
05134f3dcc
共有 11 个文件被更改,包括 490 次插入488 次删除
  1. 6 0
      Changelog-Trunk.txt
  2. 20 0
      src/common/strlib.c
  3. 4 0
      src/common/strlib.h
  4. 3 2
      src/map/atcommand.c
  5. 10 5
      src/map/clif.c
  6. 1 1
      src/map/clif.h
  7. 1 1
      src/map/map.h
  8. 426 466
      src/map/npc.c
  9. 2 3
      src/map/npc.h
  10. 11 9
      src/map/script.c
  11. 6 1
      src/map/script.h

+ 6 - 0
Changelog-Trunk.txt

@@ -4,6 +4,12 @@ AS OF SVN REV. 5091, WE ARE NOW USING TRUNK.  ALL UNTESTED BUGFIXES/FEATURES GO
 IF YOU HAVE A WORKING AND TESTED BUGFIX PUT IT INTO STABLE AS WELL AS TRUNK.
 
 2007/10/17
+	* Reworked the parsing at npc.c.
+	- Fixes npc.c discarding the '}' at the end of file, when there is no 
+	  newline. (uncovered as a side-effect of r11487)
+	* Empty script functions always have code now (won't report as missing 
+	  when you try to call them).
+	* Changed userfunc_db to not limit the name to 50 characters. [FlavioJS]
 	* Ground skill now can trigger every 20ms [Playtester]
 	- Firewall, Fire Formation and Heat now can do 50 hits a second
 	- this was proven to be official behavior, but it will raise CPU usage

+ 20 - 0
src/common/strlib.c

@@ -316,6 +316,26 @@ size_t safestrnlen(const char* string, size_t maxlen)
 	return ( string != NULL ) ? strnlen(string, maxlen) : 0;
 }
 
+/// Returns the line of the target position in the string.
+/// Lines start at 1.
+int strline(const char* str, size_t pos)
+{
+	const char* target;
+	int line;
+
+	if( str == NULL || pos == 0 )
+		return 1;
+
+	target = str+pos;
+	for( line = 1; ; ++line )
+	{
+		str = strchr(str, '\n');
+		if( str == NULL || target <= str )
+			break;// found target line
+		++str;// skip newline
+	}
+	return line;
+}
 
 /////////////////////////////////////////////////////////////////////
 // StringBuf - dynamic string

+ 4 - 0
src/common/strlib.h

@@ -37,6 +37,10 @@ char* safestrncpy(char* dst, const char* src, size_t n);
 /// doesn't crash on null pointer
 size_t safestrnlen(const char* string, size_t maxlen);
 
+/// Returns the line of the target position in the string.
+/// Lines start at 1.
+int strline(const char* str, size_t pos);
+
 /// StringBuf - dynamic string
 struct StringBuf
 {

+ 3 - 2
src/map/atcommand.c

@@ -6321,7 +6321,7 @@ int atcommand_npcmove(const int fd, struct map_session_data* sd, const char* com
 int atcommand_addwarp(const int fd, struct map_session_data* sd, const char* command, const char* message)
 {
 	char w1[64], w3[64], w4[64];
-	int x,y,ret;
+	int x,y,ret=0;
 	nullpo_retr(-1, sd);
 
 	if (!message || !*message || sscanf(message, "%23s %d %d[^\n]", atcmd_player_name, &x, &y) < 3) {
@@ -6333,7 +6333,8 @@ int atcommand_addwarp(const int fd, struct map_session_data* sd, const char* com
 	sprintf(w3,"%s%d%d%d%d", atcmd_player_name,sd->bl.x, sd->bl.y, x, y);
 	sprintf(w4,"1,1,%s.gat,%d,%d", atcmd_player_name, x, y);
 
-	ret = npc_parse_warp(w1, "warp", w3, w4);
+	// FIXME check if it failed [FlavioJS]
+	npc_parse_warp(w1, "warp", w3, w4, NULL, NULL, "console");
 
 	sprintf(atcmd_output, "New warp NPC => %s",w3);
 

+ 10 - 5
src/map/clif.c

@@ -5492,10 +5492,15 @@ void clif_vendingreport(struct map_session_data* sd, int index, int amount)
 	WFIFOW(fd,4) = amount;
 	WFIFOSET(fd,packet_len(0x137));
 }
-/*==========================================
- * ƒp�[ƒeƒB�ì�¬Š®—¹
- *------------------------------------------*/
-int clif_party_created(struct map_session_data *sd,int flag)
+
+/// Result of organizing a party.
+/// S 00FA <result>.B
+///
+/// result=0 : opens party window and shows MsgStringTable[77]="party successfully organized"
+/// result=1 : MsgStringTable[78]="party name already exists"
+/// result=2 : MsgStringTable[79]="already in a party"
+/// result=other : nothing
+int clif_party_created(struct map_session_data *sd,int result)
 {
 	int fd;
 
@@ -5504,7 +5509,7 @@ int clif_party_created(struct map_session_data *sd,int flag)
 	fd=sd->fd;
 	WFIFOHEAD(fd,packet_len(0xfa));
 	WFIFOW(fd,0)=0xfa;
-	WFIFOB(fd,2)=flag;
+	WFIFOB(fd,2)=result;
 	WFIFOSET(fd,packet_len(0xfa));
 	return 0;
 }

+ 1 - 1
src/map/clif.h

@@ -270,7 +270,7 @@ void clif_vendingreport(struct map_session_data* sd, int index, int amount);
 int clif_movetoattack(struct map_session_data *sd,struct block_list *bl);
 
 // party
-int clif_party_created(struct map_session_data *sd,int flag);
+int clif_party_created(struct map_session_data *sd,int result);
 int clif_party_member_info(struct party_data *p, struct map_session_data *sd);
 int clif_party_info(struct party_data *p, struct map_session_data *sd);
 int clif_party_invite(struct map_session_data *sd,struct map_session_data *tsd);

+ 1 - 1
src/map/map.h

@@ -850,7 +850,7 @@ struct npc_data {
 			struct npc_label_list *label_list;
 			int src_id;
 		} scr;
-		struct npc_item_list shop_item[1];
+		struct npc_item_list shop_item[1];// dynamic array, allocated with extra entries (last one has nameid 0)
 		struct {
 			short xs,ys;
 			short x,y;

+ 426 - 466
src/map/npc.c

@@ -47,7 +47,6 @@ static int npc_script=0;
 static int npc_mob=0;
 static int npc_delay_mob=0;
 static int npc_cache_mob=0;
-const char *current_file = NULL;
 int npc_get_new_npc_id(void){ return npc_id++; }
 
 static struct dbt *ev_db;
@@ -1420,31 +1419,32 @@ void npc_delsrcfile(const char* name)
 	}
 }
 
-/*==========================================
- * warp行解析
- *------------------------------------------*/
-int npc_parse_warp(char* w1, char* w2, char* w3, char* w4)
+/// Parses a warp npc.
+const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	int x, y, xs, ys, to_x, to_y, m;
-	int i;
-	char mapname[MAP_NAME_LENGTH_EXT], to_mapname[MAP_NAME_LENGTH_EXT];
+	unsigned short i;
+	char mapname[32], to_mapname[32];
 	struct npc_data *nd;
 
-	// 引数の個数チェック
-	if (sscanf(w1, "%15[^,],%d,%d", mapname, &x, &y) != 3 ||
-	   sscanf(w4, "%d,%d,%15[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5) {
-		ShowError("bad warp line : %s\n", w3);
-		return 1;
+	// w1=<from map name>,<fromX>,<fromY>,<facing>
+	// w4=<spanx>,<spany>,<to map name>,<toX>,<toY>
+	if( sscanf(w1, "%31[^,],%d,%d", mapname, &x, &y) != 3
+	||	sscanf(w4, "%d,%d,%31[^,],%d,%d", &xs, &ys, to_mapname, &to_x, &to_y) != 5 )
+	{
+		ShowError("npc_parse_warp: Invalid warp definition in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
 	}
 
 	m = map_mapname2mapid(mapname);
 	i = mapindex_name2id(to_mapname);
-	if (!i) {
-		ShowError("bad warp line (destination map not found): %s\n", w3);
-		return 1;
+	if( i == 0 )
+	{
+		ShowError("npc_parse_warp: Unknown destination map in file '%s', line '%d' : %s\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), to_mapname, w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
 	}
-		
-	nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data));
+
+	CREATE(nd, struct npc_data, 1);
 
 	nd->bl.id = npc_get_new_npc_id();
 	nd->n = map_addnpc(m, nd);
@@ -1452,16 +1452,16 @@ int npc_parse_warp(char* w1, char* w2, char* w3, char* w4)
 	nd->bl.m = m;
 	nd->bl.x = x;
 	nd->bl.y = y;
-	strncpy(nd->name, w3, NAME_LENGTH);
-	strncpy(nd->exname, w3, NAME_LENGTH);
+	safestrncpy(nd->name, w3, sizeof(nd->name));
+	safestrncpy(nd->exname, w3, sizeof(nd->exname));
 
 	if (!battle_config.warp_point_debug)
 		nd->class_ = WARP_CLASS;
 	else
 		nd->class_ = WARP_DEBUG_CLASS;
 	nd->speed = 200;
-	
-	nd->u.warp.mapindex = (short)i;
+
+	nd->u.warp.mapindex = i;
 	xs += 2;
 	ys += 2;
 	nd->u.warp.x = to_x;
@@ -1479,83 +1479,83 @@ int npc_parse_warp(char* w1, char* w2, char* w3, char* w4)
 	clif_spawn(&nd->bl);
 	strdb_put(npcname_db, nd->name, nd);
 
-	return 0;
+	return strchr(start,'\n');// continue
 }
 
-/*==========================================
- * shop行解析
- *------------------------------------------*/
-static int npc_parse_shop(char* w1, char* w2, char* w3, char* w4)
+/// Parses a shop npc.
+static const char* npc_parse_shop(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	#define MAX_SHOPITEM 100
+	struct npc_item_list items[MAX_SHOPITEM];
 	char *p;
-	int x, y, dir, m, pos = 0;
+	int x, y, dir, m, i;
 	struct npc_data *nd;
 
-	if (strcmp(w1, "-") == 0) {
+	if( strcmp(w1,"-") == 0 )
+	{// 'floating' shop?
 		x = 0; y = 0; dir = 0; m = -1;
-	} else {
-		// 引数の個数チェック
-		char mapname[MAP_NAME_LENGTH_EXT];
-		if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 || strchr(w4, ',') == NULL) {
-			ShowError("bad shop line : %s\n", w3);
-			return 1;
+	}
+	else
+	{// w1=<map name>,<x>,<y>,<facing>
+		char mapname[32];
+		if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4
+		||	strchr(w4, ',') == NULL )
+		{
+			ShowError("npc_parse_shop: Invalid shop definition in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+			return strchr(start,'\n');// skip and continue
 		}
 		m = map_mapname2mapid(mapname);
 	}
 
-	nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * (MAX_SHOPITEM + 1));
-	p = strchr(w4, ',');
-
-	while (p && pos < MAX_SHOPITEM)
+	p = strchr(w4,',');
+	for( i = 0; i < ARRAYLENGTH(items) && p; ++i )
 	{
 		int nameid, value;
-		struct item_data *id;
-		p++;
-		if (sscanf(p, "%d:%d", &nameid, &value) != 2)
+		struct item_data* id;
+		if( sscanf(p, ",%d:%d", &nameid, &value) != 2 )
+		{
+			ShowError("npc_parse_shop: Invalid item definition in file '%s', line '%d'. Ignoring the rest of the line...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
 			break;
-		nd->u.shop_item[pos].nameid = nameid;
+		}
 		id = itemdb_search(nameid);
-		if (value < 0)
+		if( value < 0 )
 			value = id->value_buy;
-		nd->u.shop_item[pos].value = value;
-		// check for bad prices that can possibly cause exploits
-		if (value/124. < id->value_sell/75.) {  //Clened up formula to prevent overflows.
-			if (value < id->value_sell)
-				ShowWarning ("Item %s [%d] buying price (%d) is less than selling price (%d) at %s\n",
-					id->name, id->nameid, value, id->value_sell, current_file);
-			else
-				ShowWarning ("Item %s [%d] discounted buying price (%d) is less than overcharged selling price (%d) at %s\n",
-					id->name, id->nameid, value/100*75, id->value_sell/100*124, current_file);
+		if( value*0.75 < id->value_sell*1.24 )
+		{// Expoit possible: you can buy and sell back with profit
+			ShowWarning("npc_parse_shop: Item %s [%d] discounted buying price (%d->%d) is less than overcharged selling price (%d->%d) at file '%s', line '%d'.\n",
+				id->name, nameid, value, (int)(value*0.75), id->value_sell, (int)(id->value_sell*1.24), filepath, strline(buffer,start-buffer));
 		}
 		//for logs filters, atcommands and iteminfo script command
-		if (id->maxchance<=0)
+		if( id->maxchance <= 0 )
 			id->maxchance = 10000; //10000 (100% drop chance)would show that the item's sold in NPC Shop
 
-		pos++;
-		p = strchr(p, ',');
+		items[i].nameid = nameid;
+		items[i].value = value;
+		p = strchr(p+1,',');
 	}
-	if (pos == 0) {
-		aFree(nd);
-		return 1;
+	if( i == 0 )
+	{
+		ShowWarning("npc_parse_shop: Ignoring empty shop in file '%s', line '%d'.\n", filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// continue
 	}
-	nd->u.shop_item[pos++].nameid = 0;
 
+	nd = (struct npc_data *) aCalloc (1, sizeof(struct npc_data) + sizeof(nd->u.shop_item[0])*(i));
+	memcpy(&nd->u.shop_item, items, sizeof(struct npc_item_list)*i);
+	nd->u.shop_item[i].nameid = 0;
 	nd->bl.prev = nd->bl.next = NULL;
 	nd->bl.m = m;
 	nd->bl.x = x;
 	nd->bl.y = y;
 	nd->bl.id = npc_get_new_npc_id();
-	safestrncpy(nd->name, w3, NAME_LENGTH);
+	safestrncpy(nd->name, w3, sizeof(nd->name));
 	nd->class_ = m==-1?-1:atoi(w4);
 	nd->speed = 200;
-	
-	nd = (struct npc_data *)aRealloc(nd, sizeof(struct npc_data) + sizeof(nd->u.shop_item[0]) * pos);
 
-	npc_shop++;
+	++npc_shop;
 	nd->bl.type = BL_NPC;
 	nd->bl.subtype = SHOP;
-	if (m >= 0) {
+	if( m >= 0 )
+	{// normal shop npc
 		nd->n = map_addnpc(m,nd);
 		map_addblock(&nd->bl);
 		status_set_viewdata(&nd->bl, nd->class_);
@@ -1564,11 +1564,12 @@ static int npc_parse_shop(char* w1, char* w2, char* w3, char* w4)
 		nd->ud.dir = dir;
 		clif_spawn(&nd->bl);
 	} else
-		// we skip map_addnpc, but still add it to the list of ID's
+	{// 'floating' shop?
 		map_addiddb(&nd->bl);
-	strdb_put(npcname_db, nd->name,nd);
+	}
+	strdb_put(npcname_db, nd->name, nd);
 
-	return 0;
+	return strchr(start,'\n');// continue
 }
 
 /*==========================================
@@ -1576,256 +1577,218 @@ static int npc_parse_shop(char* w1, char* w2, char* w3, char* w4)
  *------------------------------------------*/
 int npc_convertlabel_db(DBKey key, void* data, va_list ap)
 {
-	const char *lname = (const char*)key.str;
-	int pos = (int)data;
-	struct npc_data *nd;
-	struct npc_label_list *lst;
-	int num;
+	const char* lname = (const char*)key.str;
+	int lpos = (int)data;
+	struct npc_label_list** label_list;
+	int* label_list_num;
+	const char* filepath;
+	struct npc_label_list* label;
 	const char *p;
 	int len;
 
 	nullpo_retr(0, ap);
-	nullpo_retr(0, nd = va_arg(ap,struct npc_data *));
+	nullpo_retr(0, label_list = va_arg(ap,struct npc_label_list**));
+	nullpo_retr(0, label_list_num = va_arg(ap,int*));
+	nullpo_retr(0, filepath = va_arg(ap,const char*));
 
-	lst = nd->u.scr.label_list;
-	num = nd->u.scr.label_list_num;
-	if (!lst) {
-		lst = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
-		num = 0;
+	if( *label_list == NULL )
+	{
+		*label_list = (struct npc_label_list *) aCallocA (1, sizeof(struct npc_label_list));
+		*label_list_num = 0;
 	} else
-		lst = (struct npc_label_list *) aRealloc (lst, sizeof(struct npc_label_list)*(num+1));
+		*label_list = (struct npc_label_list *) aRealloc (*label_list, sizeof(struct npc_label_list)*(*label_list_num+1));
+	label = *label_list+*label_list_num;
 
 	// In case of labels not terminated with ':', for user defined function support
 	p = lname;
 	while( ISALNUM(*p) || *p == '_' )
-		p++;
+		++p;
 	len = p-lname;
 
 	// here we check if the label fit into the buffer
-	if (len > 23) {
-		ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, current_file);
+	if( len > 23 )
+	{
+		ShowError("npc_parse_script: label name longer than 23 chars! '%s'\n (%s)", lname, filepath);
 		exit(EXIT_FAILURE);
 	}
-	safestrncpy(lst[num].name, lname, NAME_LENGTH);
-	lst[num].pos = pos;
-	nd->u.scr.label_list = lst;
-	nd->u.scr.label_list_num = num+1;
+	safestrncpy(label->name, lname, sizeof(label->name));
+	label->pos = lpos;
+	++(*label_list_num);
 
 	return 0;
 }
 
-/*==========================================
- * parses a script line
- *------------------------------------------*/
-static void npc_parse_script_line(char* p, int* curly_count, int line)
+// Skip the contents of a script.
+static const char* npc_skip_script(const char* start, const char* buffer, const char* filepath)
 {
-	int i = strlen(p),j;
-	int string_flag = 0;
-	static int comment_flag = 0;
-	for(j = 0; j < i; j++)
+	const char* p;
+	int curly_count;
+
+	if( start == NULL )
+		return NULL;// nothing to skip
+
+	// initial bracket (assumes the previous part is ok)
+	p = strchr(start,'{');
+	if( p == NULL )
 	{
-		if(comment_flag) {
-			if(p[j] == '*' && p[j+1] == '/') { // end of multiline comment
-				(*curly_count)--;
-				comment_flag = 0;
-				j++;
-			}
-		} else if(string_flag) {
-			if(p[j] == '"') {
-				string_flag = 0;
-			} else if(p[j] == '\\' && (unsigned char)(p[j-1])<=0x7e) { // escape
-				j++;
-			}
-		} else {
-			if(p[j] == '"') {
-				string_flag = 1;
-			} else if(p[j] == '}') {
-				if(*curly_count == 0) {
-					break;
-				} else {
-					(*curly_count)--;
+		ShowError("npc_skip_script: Missing left curly in file '%s', line'%d'.", filepath, strline(buffer,start-buffer));
+		return NULL;// can't continue
+	}
+
+	// skip everything
+	for( curly_count = 1; curly_count > 0 ; )
+	{
+		p = skip_space(p+1) ;
+		if( *p == '}' )
+		{// right curly
+			--curly_count;
+		}
+		else if( *p == '{' )
+		{// left curly
+			++curly_count;
+		}
+		else if( *p == '"' )
+		{// string
+			for( ++p; *p != '"' ; ++p )
+			{
+				if( *p == '\\' && (unsigned char)p[-1] <= 0x7e )
+					++p;// escape sequence (not part of a multibyte character)
+				else if( *p == '\0' )
+				{
+					script_error(buffer, filepath, 0, "Unexpected end of string.", p);
+					return NULL;// can't continue
+				}
+				else if( *p == '\n' )
+				{
+					script_error(buffer, filepath, 0, "Unexpected newline at string.", p);
+					return NULL;// can't continue
 				}
-			} else if(p[j] == '{') {
-				(*curly_count)++;
-			} else if(p[j] == '/' && p[j+1] == '/') { // comment
-				break;
-			} else if(p[j] == '/' && p[j+1] == '*') { // multiline comment start
-				(*curly_count)++;
-				comment_flag = 1;
-				j++;
 			}
 		}
+		else if( *p == '\0' )
+		{// end of buffer
+			ShowError("Missing %d right curlys at file '%s', line '%d'.\n", curly_count, filepath, strline(buffer,p-buffer));
+			return NULL;// can't continue
+		}
 	}
-	if(string_flag) {
-		printf("Missing '\"' at file %s line %d\n", current_file, line);
-		exit(EXIT_FAILURE);
-	}
-}
 
-// Like npc_parse_script, except it's sole use is to skip the contents of a script. [Skotlex]
-static int npc_skip_script(char* w1, char* w2, char* w3, char* w4, char* first_line, FILE* fp, int* lines)
-{
-	char *srcbuf = NULL;
-	int srcsize = 65536;
-	int startline = 0;
-	char line[1024];
-	int curly_count = 0;
-	
-	srcbuf = (char *)aMallocA(srcsize*sizeof(char));
-	if (strchr(first_line, '{')) {
-		strcpy(srcbuf, strchr(first_line, '{'));
-		startline = *lines;
-	} else
-		srcbuf[0] = 0;
-	npc_parse_script_line(srcbuf,&curly_count,*lines);
-	while (curly_count > 0) {
-		fgets(line, sizeof(line), fp);
-		(*lines)++;
-		npc_parse_script_line(line,&curly_count,*lines);
-		if (feof(fp))
-			break;
-		if (strlen(srcbuf) + strlen(line) + 1 >= (size_t)srcsize) {
-			srcsize += 65536;
-			srcbuf = (char *)aRealloc(srcbuf, srcsize);
-			memset(srcbuf + srcsize - 65536, '\0', 65536);
-		}
-		if (srcbuf[0] != '{') {
-			if (strchr(line, '{')) {
-				strcpy(srcbuf, strchr(line, '{'));
-				startline = *lines;
-			}
-		} else
-			strcat(srcbuf, line);
-	}
-	if(curly_count > 0)
-		ShowError("Missing right curly at file %s, line %d\n",current_file, *lines);
-	aFree(srcbuf);
-	return 0;
+	return p+1;// return after the last '}'
 }
 
-static int npc_parse_script(char* w1, char* w2, char* w3, char* w4, char* first_line, FILE* fp, int* lines, const char* file)
+static const char* npc_parse_script(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	int x, y, dir = 0, m, xs = 0, ys = 0, class_ = 0;	// [Valaris] thanks to fov
-	char mapname[MAP_NAME_LENGTH_EXT];
-	char *srcbuf = NULL;
+	char mapname[32];
 	struct script_code *script;
-	int srcsize = 65536;
-	int startline = 0;
-	char line[1024];
 	int i;
-	struct npc_data *nd, *dnd;
-	DB label_db;
 	char *p;
-	struct npc_label_list *label_dup = NULL;
-	int label_dupnum = 0;
-	int src_id = 0;
 
-	if (strcmp(w1, "-") == 0) {
-		x = 0; y = 0; m = -1;
-	} else {
-		// 引数の個数チェック
-		if (sscanf(w1, "%15[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4 ||
-			(strcmp(w2, "script") == 0 && strchr(w4,',') == NULL)) {
-			ShowError("bad script line (in file %s): %s\n", file, w3);
-			return 1;
+	struct npc_label_list* label_list;
+	int label_list_num;
+	int src_id;
+	struct npc_data* nd;
+	struct npc_data* dnd;
+
+	if( strcmp(w1, "-") == 0 )
+	{// floating npc
+		x = 0;
+		y = 0;
+		m = -1;
+	}
+	else
+	{// npc in a map
+		if( sscanf(w1, "%31[^,],%d,%d,%d", mapname, &x, &y, &dir) != 4
+		||	(strcmp(w2, "script") == 0 && strchr(w4,',') == NULL) )
+		{
+			ShowError("npc_parse_script: unkown format for a script in file '%s', line '%d'. Skipping the rest of file...\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+			return NULL;// unknown format, don't continue
 		}
 		m = map_mapname2mapid(mapname);
 	}
 
-	if (strcmp(w2, "script") == 0){
-		// parsing script with curly
-		int curly_count = 0;
-		srcbuf = (char *)aMallocA(srcsize*sizeof(char));
-		if (strchr(first_line, '{')) {
-			strcpy(srcbuf, strchr(first_line, '{'));
-			startline = *lines;
-		} else
-			srcbuf[0] = 0;
-		npc_parse_script_line(srcbuf,&curly_count,*lines);
-		while (curly_count > 0) {
-			fgets(line, sizeof(line), fp);
-			(*lines)++;
-			npc_parse_script_line(line,&curly_count,*lines);
-			if (feof(fp))
-				break;
-			if (strlen(srcbuf) + strlen(line) + 1 >= (size_t)srcsize) {
-				srcsize += 65536;
-				srcbuf = (char *)aRealloc(srcbuf, srcsize);
-				memset(srcbuf + srcsize - 65536, '\0', 65536);
-			}
-			if (srcbuf[0] != '{') {
-				if (strchr(line, '{')) {
-					strcpy(srcbuf, strchr(line, '{'));
-					startline = *lines;
-				}
-			} else
-				strcat(srcbuf, line);
-		}
-		if(curly_count > 0) {
-			ShowError("Missing right curly at file %s, line %d\n", file, *lines);
-			script = NULL;
-		} else {
-			// printf("Ok line %d\n",*lines);
-			script = parse_script(srcbuf, file, startline, SCRIPT_USE_LABEL_DB);
-		}
-		if (script == NULL) {
-			// script parse error?
-			aFree(srcbuf);
-			return 1;
+	if( strcmp(w2, "script") == 0 )
+	{// parsing script with curly
+		const char* real_start;
+		const char* end;
+
+		end = npc_skip_script(start, buffer, filepath);
+		if( end == NULL )
+			return NULL;// (simple) parse error, don't continue
+
+		real_start = strchr(start,'{');
+		script = parse_script(real_start, filepath, strline(buffer,real_start-buffer), SCRIPT_USE_LABEL_DB);
+		label_list = NULL;
+		label_list_num = 0;
+		src_id = 0;
+		if( script )
+		{
+			DB label_db = script_get_label_db();
+			label_db->foreach(label_db, npc_convertlabel_db, &label_list, &label_list_num, filepath);
+			label_db->clear(label_db, NULL); // not needed anymore, so clear the db
 		}
-	} else {
-		// duplicateする
+	}
+	else
+	{// duplicate npc
 		char srcname[128];
-		struct npc_data *dnd;
-		if (sscanf(w2, "duplicate(%[^)])", srcname) != 1) {
-			ShowError("bad duplicate name (in %s)! : %s", file, w2);
-			return 0;
+		if( sscanf(w2,"duplicate(%127[^)])",srcname) != 1 )
+		{
+			ShowError("npc_parse_script: bad duplicate name in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), w2);
+			return strchr(start, '\n');// next line, try to continue
 		}
-		if ((dnd = npc_name2id(srcname)) == NULL) {
-			ShowError("bad duplicate name (in %s)! (not exist) : %s\n", file, srcname);
-			return 0;
+		dnd = npc_name2id(srcname);
+		if( dnd == NULL) {
+			ShowError("npc_parse_script: original npc not found for duplicate in file '%s', line '%d' : %s\n", filepath, strline(buffer,start-buffer), srcname);
+			return strchr(start, '\n');// next line, continue
 		}
 		script = dnd->u.scr.script;
-		label_dup = dnd->u.scr.label_list;
-		label_dupnum = dnd->u.scr.label_list_num;
+		label_list = dnd->u.scr.label_list;// TODO duplicate this?
+		label_list_num = dnd->u.scr.label_list_num;
 		src_id = dnd->bl.id;
+	}
 
-	}// end of スクリプト解析
-
-	nd = (struct npc_data *)aCalloc(1, sizeof(struct npc_data));
-
-	if (sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3) {
-		// 接触型NPC
+	CREATE(nd, struct npc_data, 1);
 
+	if( sscanf(w4, "%d,%d,%d", &class_, &xs, &ys) == 3 )
+	{// OnTouch area defined
 		if (xs >= 0) xs = xs * 2 + 1;
 		if (ys >= 0) ys = ys * 2 + 1;
 		nd->u.scr.xs = xs;
 		nd->u.scr.ys = ys;
-	} else {
-		// クリック型NPC
+	}
+	else
+	{
 		class_ = atoi(w4);
 		nd->u.scr.xs = 0;
 		nd->u.scr.ys = 0;
 	}
 
-	p = strchr(w3,':');
-	while (p) {
-		if (p[1] == ':') break;
-		p = strchr(p+1, ':');
+	// extended name
+	p = strstr(w3,"::");
+	if( p )
+	{
+		*p = '\0';
+		safestrncpy(nd->name, w3, sizeof(nd->name));
+		safestrncpy(nd->exname, p+2, sizeof(nd->exname));
 	}
-	if (p) {
-		*p = 0;
-		memcpy(nd->name, w3, NAME_LENGTH);
-		memcpy(nd->exname, p+2, NAME_LENGTH);
-	} else {
-		memcpy(nd->name, w3, NAME_LENGTH);
-		memcpy(nd->exname, w3, NAME_LENGTH);
+	else
+	{
+		safestrncpy(nd->name, w3, sizeof(nd->name));
+		safestrncpy(nd->exname, w3, sizeof(nd->exname));
 	}
 
-	if((dnd = npc_name2id(nd->exname))){
-		if(battle_config.etc_log)
-			ShowInfo("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s' (Duplicated System Name - Lazy scripters >_>) \n",nd->name,nd->exname,nd->name,npc_script,file);
-		sprintf(nd->exname, "%d", npc_script);
+	if( npc_name2id(nd->exname) )
+	{// duplicate name, generate new name
+		char newexname[32];
+		int i = 0;
+		do
+		{
+			++i;
+			sprintf(newexname, "%31d", i);
+			dnd = npc_name2id(newexname);
+		}
+		while( dnd );
+		ShowWarning("npc_parse_script: Overriding NPC '%s::%s' to '%s::%d'.. in file '%s', line '%d' (Duplicated System Name - Lazy scripters >_>) \n", nd->name, nd->exname, nd->name, i, filepath, strline(buffer,start-buffer));
+		safestrncpy(nd->exname, newexname, sizeof(nd->exname));
 	}
 
 	nd->bl.prev = nd->bl.next = NULL;
@@ -1837,12 +1800,15 @@ static int npc_parse_script(char* w1, char* w2, char* w3, char* w4, char* first_
 	nd->speed = 200;
 	nd->u.scr.script = script;
 	nd->u.scr.src_id = src_id;
+	nd->u.scr.label_list = label_list;
+	nd->u.scr.label_list_num = label_list_num;
 
-	npc_script++;
+	++npc_script;
 	nd->bl.type = BL_NPC;
 	nd->bl.subtype = SCRIPT;
 
-	if (m >= 0) {
+	if( m >= 0 )
+	{
 		nd->n = map_addnpc(m, nd);
 		status_change_init(&nd->bl);
 		unit_dataset(&nd->bl);
@@ -1858,33 +1824,19 @@ static int npc_parse_script(char* w1, char* w2, char* w3, char* w4, char* first_
 		} else {
 			clif_spawn(&nd->bl);
 		}*/
-		if (class_ >= 0){
+		if( class_ >= 0 )
+		{
 			status_set_viewdata(&nd->bl, nd->class_);
 			clif_spawn(&nd->bl);
 		}
-	} else {
+	}
+	else
+	{
 		// we skip map_addnpc, but still add it to the list of ID's
 		map_addiddb(&nd->bl);
 	}
 	strdb_put(npcname_db, nd->exname, nd);
 
-	//-----------------------------------------
-	// ラベルデータの準備
-	if (srcbuf){
-		// script本体がある場合の処理
-		// ラベルデータのコンバート
-		label_db = script_get_label_db();
-		label_db->foreach(label_db, npc_convertlabel_db, nd);
-		label_db->clear(label_db,NULL); // not needed anymore, so clear the db
-
-		// もう使わないのでバッファ解放
-		aFree(srcbuf);
-	} else {
-		// duplicate
-		nd->u.scr.label_list = label_dup;	// ラベルデータ共有
-		nd->u.scr.label_list_num = label_dupnum;
-	}
-
 	//-----------------------------------------
 	// イベント用ラベルデータのエクスポート
 	for (i = 0; i < nd->u.scr.label_list_num; i++)
@@ -1897,13 +1849,13 @@ static int npc_parse_script(char* w1, char* w2, char* w3, char* w4, char* first_
 			struct event_data* ev;
 			char buf[50+1]; // 24 for npc name + 24 for label + 2 for a "::" and 1 for EOS
 			snprintf(buf, sizeof(buf), "%s::%s", nd->exname, lname);
-			
+
 			// generate the data and insert it
-			ev = (struct event_data *)aMalloc(sizeof(struct event_data));
+			CREATE(ev, struct event_data, 1);
 			ev->nd = nd;
 			ev->pos = pos;
-			if (strdb_put(ev_db, buf, ev) != NULL) //There was already another event of the same name?
-				ShowWarning("npc_parse_script : duplicate event %s (%s)\n", buf, file);
+			if( strdb_put(ev_db, buf, ev) != NULL )// There was already another event of the same name?
+				ShowWarning("npc_parse_script : duplicate event %s (%s)\n", buf, filepath);
 		}
 	}
 
@@ -2031,76 +1983,35 @@ int npc_changename(const char* name, const char* newname, short look)
 	return 0;
 }
 
-/*==========================================
- * function行解析
- *------------------------------------------*/
-static int npc_parse_function(char* w1, char* w2, char* w3, char* w4, char* first_line, FILE* fp, int* lines, const char* file)
+/// Parses a function.
+/// function%TAB%script%TAB%<function name>%TAB%{<code>}
+static const char* npc_parse_function(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
-	char *srcbuf, *p;
+	struct dbt *func_db;
 	struct script_code *script;
 	struct script_code *oldscript;
-	int srcsize = 65536;
-	int startline = 0;
-	char line[1024];
-	int curly_count = 0;
-	struct dbt *user_db;
-	
-	// スクリプトの解析
-	srcbuf = (char *) aMallocA (srcsize*sizeof(char));
-	if (strchr(first_line,'{')) {
-		strcpy(srcbuf, strchr(first_line,'{'));
-		startline = *lines;
-	} else
-		srcbuf[0] = 0;
-	npc_parse_script_line(srcbuf,&curly_count,*lines);
-
-	while (curly_count > 0) {
-		fgets(line, sizeof(line), fp);
-		(*lines)++;
-		npc_parse_script_line(line,&curly_count,*lines);
-		if (feof(fp))
-			break;
-		if (strlen(srcbuf)+strlen(line)+1 >= (unsigned int)srcsize) {
-			srcsize += 65536;
-			srcbuf = (char *)aRealloc(srcbuf, srcsize);
-			memset(srcbuf + srcsize - 65536, '\0', 65536);
-		}
-		if (srcbuf[0]!='{') {
-			if (strchr(line,'{')) {
-				strcpy(srcbuf, strchr(line,'{'));
-				startline = *lines;
-			}
-		} else
-			strcat(srcbuf,line);
-	}
-	if(curly_count > 0) {
-		ShowError("Missing right curly at file %s, line %d\n",file, *lines);
-		script = NULL;
-	} else {
-		script = parse_script(srcbuf, file, startline,0);
-	}
-	if (script == NULL) {
-		// script parse error?
-		aFree(srcbuf);
-		return 1;
-	}
+	const char* end;
 
-	p = (char *) aMallocA (50*sizeof(char));
-	strncpy(p, w3, 50);
+	end = npc_skip_script(start,buffer,filepath);
+	if( end == NULL )
+		return NULL;// (simple) parse error, don't continue
 
-	user_db = script_get_userfunc_db();
-	oldscript = (struct script_code *)strdb_get(user_db, p);
-	if(oldscript != NULL) {
-		ShowInfo("parse_function: Overwriting user function [%s] (%s:%d)\n", p, file, *lines);
-		script_free_vars( &oldscript->script_vars );
-		aFree( oldscript->script_buf );
-		user_db->remove(user_db,str2key(p));
+	start = strchr(start,'{');
+	script = parse_script(start, filepath, strline(buffer,start-buffer), SCRIPT_RETURN_EMPTY_SCRIPT);
+	if( script == NULL )// parse error, continue
+		return end;
+
+	func_db = script_get_userfunc_db();
+	oldscript = (struct script_code*)strdb_put(func_db, w3, script);
+	if( oldscript != NULL )
+	{
+		ShowInfo("npc_parse_function: Overwriting user function [%s] (%s:%d)\n", w3, filepath, strline(buffer,start-buffer));
+		script_free_vars(&oldscript->script_vars);
+		aFree(oldscript->script_buf);
+		aFree(oldscript);
 	}
-	strdb_put(user_db, p, script);
 
-	// もう使わないのでバッファ解放
-	aFree(srcbuf);
-	return 0;
+	return end;
 }
 
 
@@ -2128,45 +2039,51 @@ int npc_parse_mob2(struct spawn_data* mob, int index)
 	return 1;
 }
 
-int npc_parse_mob(char* w1, char* w2, char* w3, char* w4)
+static const char* npc_parse_mob(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	int level, num, class_, mode, x,y,xs,ys;
-	char mapname[MAP_NAME_LENGTH_EXT];
-	char mobname[NAME_LENGTH];
+	char mapname[32];
+	char mobname[128];
 	struct spawn_data mob, *data;
 	struct mob_db* db;
 
 	memset(&mob, 0, sizeof(struct spawn_data));
 
-	// 引数の個数チェック
-	if (sscanf(w1, "%15[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3 ||
-		sscanf(w4, "%d,%d,%u,%u,%49[^\t\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 ) {
-		ShowError("bad monster line : %s %s %s (file %s)\n", w1, w3, w4, current_file);
-		return 1;
+	// w1=<map name>,<x>,<y>,<xs>,<ys>
+	// w4=<mob id>,<amount>,<delay1>,<delay2>,<event>
+	if( sscanf(w1, "%31[^,],%d,%d,%d,%d", mapname, &x, &y, &xs, &ys) < 3
+	||	sscanf(w4, "%d,%d,%u,%u,%127[^\t\r\n]", &class_, &num, &mob.delay1, &mob.delay2, mob.eventname) < 2 )
+	{
+		ShowError("npc_parse_mob: Invalid mob definition in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
 	}
-	if (!mapindex_name2id(mapname)) {
-		ShowError("wrong map name : %s %s (file %s)\n", w1,w3, current_file);
-		return 1;
+	if( mapindex_name2id(mapname) == 0 )
+	{
+		ShowError("npc_parse_mob: Unknown map '%s' in file '%s', line '%d'.\n", mapname, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
 	}
 	mode =  map_mapname2mapid(mapname);
-	if (mode < 0) //Not loaded on this map-server instance.
-		return 1;
+	if( mode < 0 )//Not loaded on this map-server instance.
+		return strchr(start,'\n');// skip and continue
 	mob.m = (unsigned short)mode;
 
-	if (x < 0 || map[mob.m].xs <= x || y < 0 || map[mob.m].ys <= y) {
-		ShowError("Out of range spawn coordinates: %s (%d,%d), map size is (%d,%d) - %s %s (file %s)\n", map[mob.m].name, x, y, map[mob.m].xs-1, map[mob.m].ys-1, w1,w3, current_file);
-		return 1;
+	if( x < 0 || x >= map[mob.m].xs || y < 0 || y >= map[mob.m].ys )
+	{
+		ShowError("npc_parse_mob: Spawn coordinates out of range: %s (%d,%d), map size is (%d,%d) - %s %s (file '%s', line '%d').\n", map[mob.m].name, x, y, (map[mob.m].xs-1), (map[mob.m].ys-1), w1, w3, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
 	}
 
 	// check monster ID if exists!
-	if (mobdb_checkid(class_)==0) {
-		ShowError("bad monster ID : %s %s (file %s)\n", w3, w4, current_file);
-		return 1;
+	if( mobdb_checkid(class_) == 0 )
+	{
+		ShowError("npc_parse_mob: Unknown mob ID : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
 	}
 
-	if (num < 1 || num>1000 ) {
-		ShowError("wrong number of monsters : %s %s (file %s)\n", w3, w4, current_file);
-		return 1;
+	if( num < 1 || num > 1000 )
+	{
+		ShowError("npc_parse_mob: Invalid number of monsters (must be inside the range [1,1000]) : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
 	}
 
 	//Fix for previously wrong interpretation of the delays
@@ -2214,12 +2131,12 @@ int npc_parse_mob(char* w1, char* w2, char* w3, char* w4)
 	}
 
 	// parse MOB_NAME,[MOB LEVEL]
-	if (sscanf(w3, "%23[^,],%d", mobname, &level) > 1)
+	if (sscanf(w3, "%127[^,],%d", mobname, &level) > 1)
 		mob.level = level;
 
 	if(mob.delay1>0xfffffff || mob.delay2>0xfffffff) {
-		ShowError("wrong monsters spawn delays : %s %s (file %s)\n", w3, w4, current_file);
-		return 1;
+		ShowError("npc_parse_mob: wrong monsters spawn delays : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
 	}
 
 	//Use db names instead of the spawn file ones.
@@ -2230,8 +2147,11 @@ int npc_parse_mob(char* w1, char* w2, char* w3, char* w4)
 	else
 		strncpy(mob.name, mobname, NAME_LENGTH-1);
 
-	if (!mob_parse_dataset(&mob)) //Verify dataset.
-		return 1;
+	if( !mob_parse_dataset(&mob) ) //Verify dataset.
+	{
+		ShowError("npc_parse_mob: Invalid dataset : %s %s (file '%s', line '%d').\n", w3, w4, filepath, strline(buffer,start-buffer));
+		return strchr(start,'\n');// skip and continue
+	}
 
 	for(x=0; x < ARRAYLENGTH(db->spawn); x++)
 	{
@@ -2288,30 +2208,36 @@ int npc_parse_mob(char* w1, char* w2, char* w3, char* w4)
 
 	npc_mob++;
 
-	return 0;
+	return strchr(start,'\n');// continue
 }
 
 /*==========================================
  * マップフラグ行の解析
  *------------------------------------------*/
-static int npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4)
+static const char* npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	int m;
-	char mapname[MAP_NAME_LENGTH_EXT];
+	char mapname[32];
 	int state = 1;
 
-	// 引数の個数チェック
-	if (sscanf(w1, "%15[^,]",mapname) != 1)
-		return 1;
-
+	// w1=<mapname>
+	if( sscanf(w1, "%31[^,]", mapname) != 1 )
+	{
+		ShowError("npc_parse_mapflag: Invalid mapflag definition in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
+	}
 	m = map_mapname2mapid(mapname);
-	if (m < 0)
-		return 1;
+	if( m < 0 )
+	{
+		ShowWarning("npc_parse_mapflag: Unknown map in file '%s', line '%d' : %s\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", mapname, filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
+	}
+
 	if (w4 && !strcmpi(w4, "off"))
 		state = 0;	//Disable mapflag rather than enable it. [Skotlex]
 	
 	if (!strcmpi(w3, "nosave")) {
-		char savemap[MAP_NAME_LENGTH_EXT];
+		char savemap[32];
 		int savex, savey;
 		if (state == 0)
 			; //Map flag disabled.
@@ -2319,12 +2245,12 @@ static int npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4)
 			map[m].save.map = 0;
 			map[m].save.x = -1;
 			map[m].save.y = -1;
-		} else if (sscanf(w4, "%15[^,],%d,%d", savemap, &savex, &savey) == 3) {
+		} else if (sscanf(w4, "%31[^,],%d,%d", savemap, &savex, &savey) == 3) {
 			map[m].save.map = mapindex_name2id(savemap);
 			map[m].save.x = savex;
 			map[m].save.y = savey;
 			if (!map[m].save.map) {
-				ShowWarning("Specified save point map '%s' for mapflag 'nosave' not found (file %s), using 'SavePoint'.\n",savemap,current_file);
+				ShowWarning("npc_parse_mapflag: Specified save point map '%s' for mapflag 'nosave' not found (file '%s', line '%d'), using 'SavePoint'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", savemap, filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
 				map[m].save.x = -1;
 				map[m].save.y = -1;
 			}
@@ -2353,7 +2279,7 @@ static int npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4)
 		map[m].flag.pvp=state;
 		if (state) {
 			if (map[m].flag.gvg || map[m].flag.gvg_dungeon || map[m].flag.gvg_castle)
-				ShowWarning("You can't set PvP and GvG flags for the same map! Removing GvG flags from %s\n", map[m].name);
+				ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing GvG flags from %s (file '%s', line '%d').\n", map[m].name, filepath, strline(buffer,start-buffer));
 			map[m].flag.gvg=0;
 			map[m].flag.gvg_dungeon=0;
 			map[m].flag.gvg_castle=0;
@@ -2400,7 +2326,7 @@ static int npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4)
 		if (state && map[m].flag.pvp)
 		{
 			map[m].flag.pvp=0;
-			ShowWarning("You can't set PvP and GvG flags for the same map! Removing PvP flag from %s\n", map[m].name);
+			ShowWarning("npc_parse_mapflag: You can't set PvP and GvG flags for the same map! Removing PvP flag from %s (file '%s', line '%d').\n", map[m].name, filepath, strline(buffer,start-buffer));
 		}
 	}
 	else if (!strcmpi(w3,"gvg_noparty"))
@@ -2503,125 +2429,159 @@ static int npc_parse_mapflag(char* w1, char* w2, char* w3, char* w4)
 	else if (!strcmpi(w3,"guildlock"))
 		map[m].flag.guildlock=state;
 
-	return 0;
+	return strchr(start,'\n');// continue
 }
 
 /*==========================================
  * Setting up map cells
  *------------------------------------------*/
-static int npc_parse_mapcell(char* w1, char* w2, char* w3, char* w4)
+static const char* npc_parse_mapcell(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath)
 {
 	int m, cell, x, y, x0, y0, x1, y1;
-	char type[24], mapname[MAP_NAME_LENGTH_EXT];
-
-	if (sscanf(w1, "%15[^,]", mapname) != 1)
-		return 1;
+	char type[32], mapname[32];
 
+	// w1=<mapname>
+	// w3=<type>,<x1>,<y1>,<x2>,<y2>
+	if( sscanf(w1, "%31[^,]", mapname) != 1
+	||	sscanf(w3, "%31[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 5 )
+	{
+		ShowError("npc_parse_mapcell: Invalid mapcell definition in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
+	}
 	m = map_mapname2mapid(mapname);
-	if (m < 0)
-		return 1;
-
-	if (sscanf(w3, "%23[^,],%d,%d,%d,%d", type, &x0, &y0, &x1, &y1) < 5) {
-		ShowError("Bad setcell line : %s\n",w3);
-		return 1;
+	if( m < 0 )
+	{
+		ShowWarning("npc_parse_mapcell: Unknown map in file '%s', line '%d' : %s\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", mapname, filepath, strline(buffer,start-buffer), w1, w2, w3, w4);
+		return strchr(start,'\n');// skip and continue
 	}
+
 	cell = strtol(type, (char **)NULL, 0);
-	//printf ("0x%x %d %d %d %d\n", cell, x0, y0, x1, y1);
 
-	if (x0 > x1) { int t = x0; x0 = x1; x1 = t; }
-	if (y0 > y1) { int t = y0; y0 = y1; y1 = t; }
+	if( x0 > x1 )
+		swap(x0, x1);
+	if( y0 > y1 )
+		swap(y0, y1);
 
-	for (x = x0; x <= x1; x++) {
-		for (y = y0; y <= y1; y++) {
+	for( x = x0; x <= x1; ++x )
+		for( y = y0; y <= y1; ++y )
 			map_setcell(m, x, y, cell);
-			//printf ("setcell 0x%x %d %d %d\n", cell, m, x, y);
-		}
-	}
 
-	return 0;
+	return strchr(start,'\n');// continue
 }
 
-void npc_parsesrcfile(const char* name)
+void npc_parsesrcfile(const char* filepath)
 {
 	int m, lines = 0;
-	char line[2048];
-
-	FILE *fp = fopen (name,"r");
-	if (fp == NULL) {
-		ShowError ("File not found : %s\n", name);
+	FILE* fp;
+	size_t len;
+	char* buffer;
+	const char* p;
+
+	// read whole file to buffer
+	fp = fopen(filepath, "rb");
+	if( fp == NULL )
+	{
+		ShowError("npc_parsesrcfile: File not found '%s'.\n", filepath);
+		return;
+	}
+	fseek(fp, 0, SEEK_END);
+	len = ftell(fp);
+	buffer = (char*)aMalloc(len+1);
+	buffer[len] = '\0';
+	fseek(fp, 0, SEEK_SET);
+	if( fread(buffer, len, 1, fp) != 1 )
+	{
+		ShowError("npc_parsesrcfile: Failed to read file '%s'.\n", filepath);
+		aFree(buffer);
+		fclose(fp);
 		return;
 	}
-	current_file = name;
+	fclose(fp);
 
-	while (fgets(line, sizeof(line), fp))
+	// parse buffer
+	for( p = skip_space(buffer); p && *p ; p = skip_space(p) )
 	{
 		char w1[2048], w2[2048], w3[2048], w4[2048], mapname[2048];
 		int i, w4pos, count;
 		lines++;
 
-		if (line[0] == '/' && line[1] == '/')
-			continue;
-
-		if (!sscanf(line, " %n", &i) && i == strlen(line)) // just whitespace
-			continue;
+		w1[0] = w2[0] = w3[0] = w4[0] = '\0';
 
-		// 最初はタブ区切りでチェックしてみて、ダメならスペース区切りで確認
-		w1[0] = w2[0] = w3[0] = w4[0] = '\0'; //It's best to initialize values
-		//to prevent passing previously parsed values to the parsers when not all
-		//fields are specified. [Skotlex]
-		if ((count = sscanf(line, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3)
+		// w1<TAB>w2<TAB>w3<TAB>w4
+		if( (count = sscanf(p, "%[^\t\r\n]\t%[^\t\r\n]\t%[^\t\r\n]\t%n%[^\r\n]", w1, w2, w3, &w4pos, w4)) < 3 )
 		{
-			if ((count = sscanf(line, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4 ||
-			(count = sscanf(line, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3)
-			{
-				ShowWarning("Incorrect separator syntax in file '%s', line '%i'. Use tabs instead of spaces!\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
-			} else {
-				ShowError("Could not parse file '%s', line '%i'.\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
+			if( (count = sscanf(p, "%s %s %[^\t]\t %n%[^\n]", w1, w2, w3, &w4pos, w4)) == 4
+			||	(count = sscanf(p, "%s %s %s %n%[^\n]\n", w1, w2, w3, &w4pos, w4)) >= 3 )
+			{// Incorrect syntax, try to parse
+				ShowWarning("npc_parsesrcfile: Incorrect separator syntax in file '%s', line '%d'. Use tabs instead of spaces!\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,p-buffer), w1, w2, w3, w4);
+			}
+			else
+			{// Unknown syntax, try to continue
+				ShowError("npc_parsesrcfile: Unknown syntax in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,p-buffer), w1, w2, w3, w4);
+				p = strchr(p,'\n');// next line
 				continue;
 			}
 		}
 
-		// マップの存在確認
-		if (strcmp(w1,"-") !=0 && strcmpi(w1,"function") != 0 ){
+		if( strcmp(w1,"-") !=0 && strcasecmp(w1,"function") != 0 )
+		{// w1 = <map name>,<x>,<y>,<facing>
 			sscanf(w1,"%[^,]",mapname);
-			if (!mapindex_name2id(mapname)) { //Incorrect map
-				ShowError("Invalid map '%s' in line %d, file %s\n", mapname, lines, current_file);
-				if (strcmpi(w2,"script") == 0 && count > 3)	//we must skip the script info...
-					npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+			if( !mapindex_name2id(mapname) )
+			{// Incorrect map, we must skip the script info...
+				ShowError("npc_parsesrcfile: Unknown map '%s' in file '%s', line '%d'.\n", mapname, filepath, strline(buffer,p-buffer));
+				if( strcasecmp(w2,"script") == 0 && count > 3 )
+					p = npc_skip_script(p,buffer,filepath);
+				p = strchr(p,'\n');// next line
 				continue;
 			}
-			if ((m = map_mapname2mapid(mapname)) < 0) {
-			// "mapname" is not assigned to this server
-			// we must skip the script info...
-				if (strcmpi(w2,"script") == 0 && count > 3)
-					npc_skip_script(w1,w2,w3,w4,line+w4pos,fp,&lines);
+			m = map_mapname2mapid(mapname);
+			if( m < 0 )
+			{// "mapname" is not assigned to this server, we must skip the script info...
+				if( strcasecmp(w2,"script") == 0 && count > 3 )
+					p = npc_skip_script(p,buffer,filepath);
+				p = strchr(p,'\n');// next line
 				continue;
 			}
 		}
-		if (strcmpi(w2,"warp") == 0 && count > 3) {
-			npc_parse_warp(w1,w2,w3,w4);
-		} else if (strcmpi(w2,"shop") == 0 && count > 3) {
-			npc_parse_shop(w1,w2,w3,w4);
-		} else if (strcmpi(w2,"script") == 0 && count > 3) {
-			if (strcmpi(w1,"function") == 0) {
-				npc_parse_function(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
-			} else {
-				npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
-			}
-		} else if ((i = 0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3) {
-			npc_parse_script(w1,w2,w3,w4,line+w4pos,fp,&lines,name);
-		} else if (strcmpi(w2,"monster") == 0 && count > 3) {
-			npc_parse_mob(w1,w2,w3,w4);
-		} else if (strcmpi(w2,"mapflag") == 0 && count >= 3) {
-			npc_parse_mapflag(w1,w2,w3,w4);
-		} else if (strcmpi(w2,"setcell") == 0 && count >= 3) {
-			npc_parse_mapcell(w1,w2,w3,w4);
-		} else {
-			ShowError("Probably TAB is missing in file '%s', line '%i':\n * %s %s %s %s\n",current_file,lines,w1,w2,w3,w4);
+
+		if( strcasecmp(w2,"warp") == 0 && count > 3 )
+		{
+			p = npc_parse_warp(w1,w2,w3,w4, p, buffer, filepath);
+		}
+		else if( strcasecmp(w2,"shop") == 0 && count > 3 )
+		{
+			p = npc_parse_shop(w1,w2,w3,w4, p, buffer, filepath);
+		}
+		else if( strcasecmp(w2,"script") == 0 && count > 3 )
+		{
+			if( strcasecmp(w1,"function") == 0 )
+				p = npc_parse_function(w1, w2, w3, w4, p, buffer, filepath);
+			else
+				p = npc_parse_script(w1,w2,w3,w4, p, buffer, filepath);
+		}
+		else if( (i=0, sscanf(w2,"duplicate%n",&i), (i > 0 && w2[i] == '(')) && count > 3 )
+		{
+			p = npc_parse_script(w1,w2,w3,w4, p, buffer, filepath);
+		}
+		else if( strcmpi(w2,"monster") == 0 && count > 3 )
+		{
+			p = npc_parse_mob(w1, w2, w3, w4, p, buffer, filepath);
+		}
+		else if( strcmpi(w2,"mapflag") == 0 && count >= 3 )
+		{
+			p = npc_parse_mapflag(w1, w2, w3, w4, p, buffer, filepath);
+		}
+		else if( strcmpi(w2,"setcell") == 0 && count >= 3 )
+		{
+			p = npc_parse_mapcell(w1, w2, w3, w4, p, buffer, filepath);
+		}
+		else
+		{
+			ShowError("Probably TAB is missing in file '%s', line '%d'.\n * w1=%s\n * w2=%s\n * w3=%s\n * w4=%s\n", filepath, strline(buffer,p-buffer), w1, w2, w3, w4);
+			p = strchr(p,'\n');// skip and continue
 		}
 	}
-	fclose(fp);
-	current_file = NULL;
+	aFree(buffer);
 
 	return;
 }

+ 2 - 3
src/map/npc.h

@@ -52,9 +52,8 @@ int npc_checknear2(struct map_session_data* sd, struct block_list* bl);
 int npc_buysellsel(struct map_session_data* sd, int id, int type);
 int npc_buylist(struct map_session_data* sd,int n, unsigned short* item_list);
 int npc_selllist(struct map_session_data* sd, int n, unsigned short* item_list);
-int npc_parse_mob(char* w1, char* w2, char* w3, char* w4);
 int npc_parse_mob2(struct spawn_data* mob, int index); // [Wizputer]
-int npc_parse_warp(char* w1,char* w2,char* w3,char* w4);
+const char* npc_parse_warp(char* w1, char* w2, char* w3, char* w4, const char* start, const char* buffer, const char* filepath);
 int npc_globalmessage(const char* name,const char* mes);
 
 void npc_setcells(struct npc_data* nd);
@@ -67,7 +66,7 @@ int npc_get_new_npc_id(void);
 
 void npc_addsrcfile(const char* name);
 void npc_delsrcfile(const char* name);
-void npc_parsesrcfile(const char* name);
+void npc_parsesrcfile(const char* filepath);
 int do_final_npc(void);
 int do_init_npc(void);
 int npc_event_do_oninit(void);

+ 11 - 9
src/map/script.c

@@ -722,9 +722,10 @@ void set_label(int l,int pos, const char* script_pos)
 }
 
 /// Skips spaces and/or comments.
-static
 const char* skip_space(const char* p)
 {
+	if( p == NULL )
+		return NULL;
 	for(;;)
 	{
 		while( ISSPACE(*p) )
@@ -740,7 +741,7 @@ const char* skip_space(const char* p)
 			for(;;)
 			{
 				if( *p == '\0' )
-					disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p);
+					return p;//disp_error_message("script:skip_space: end of file while parsing block comment. expected "CL_BOLD"*/"CL_NORM, p);
 				if( *p == '*' && p[1] == '/' )
 				{// end of block comment
 					p += 2;
@@ -1896,7 +1897,7 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o
 {
 	const char *p,*tmpp;
 	int i;
-	struct script_code *code;
+	struct script_code* code = NULL;
 	static int first=1;
 	char end;
 
@@ -1955,8 +1956,8 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o
 	p=skip_space(p);
 	if( options&SCRIPT_IGNORE_EXTERNAL_BRACKETS )
 	{// does not require brackets around the script
-		if( *p == '\0' )
-		{// empty script
+		if( *p == '\0' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+		{// empty script and can return NULL
 			aFree( script_buf );
 			script_pos  = 0;
 			script_size = 0;
@@ -1969,9 +1970,9 @@ struct script_code* parse_script(const char *src,const char *file,int line,int o
 	{// requires brackets around the script
 		if( *p != '{' )
 			disp_error_message("not found '{'",p);
-		p = skip_space(++p);
-		if( *p == '}' )
-		{// empty script
+		p = skip_space(p+1);
+		if( *p == '}' && !(options&SCRIPT_RETURN_EMPTY_SCRIPT) )
+		{// empty script and can return NULL
 			aFree( script_buf );
 			script_pos  = 0;
 			script_size = 0;
@@ -3586,6 +3587,7 @@ static int do_final_userfunc_sub (DBKey key,void *data,va_list ap)
 	if(code){
 		script_free_vars( &code->script_vars );
 		aFree( code->script_buf );
+		aFree( code );
 	}
 	return 0;
 }
@@ -3683,7 +3685,7 @@ int do_init_script()
 {
 	mapreg_db= db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_BASE,sizeof(int));
 	mapregstr_db=db_alloc(__FILE__,__LINE__,DB_INT,DB_OPT_RELEASE_DATA,sizeof(int));
-	userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_RELEASE_BOTH,50);
+	userfunc_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY,0);
 	scriptlabel_db=db_alloc(__FILE__,__LINE__,DB_STRING,DB_OPT_DUP_KEY|DB_OPT_ALLOW_NULL_DATA,50);
 	
 	script_load_mapreg();

+ 6 - 1
src/map/script.h

@@ -63,9 +63,14 @@ struct script_state {
 
 enum script_parse_options {
 	SCRIPT_USE_LABEL_DB = 0x1,// records labels in scriptlabel_db
-	SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2// ignores the check for {} brackets around the script
+	SCRIPT_IGNORE_EXTERNAL_BRACKETS = 0x2,// ignores the check for {} brackets around the script
+	SCRIPT_RETURN_EMPTY_SCRIPT = 0x4// returns the script object instead of NULL for empty scripts
 };
 
+const char* skip_space(const char* p);
+const char* script_print_line(const char* p, const char* mark, int line);
+void script_error(const char *src,const char *file,int start_line, const char *error_msg, const char *error_pos);
+
 struct script_code* parse_script(const char* src,const char* file,int line,int options);
 void run_script_sub(struct script_code *rootscript,int pos,int rid,int oid, char* file, int lineno);
 void run_script(struct script_code*,int,int,int);