(svn r4439) - NewGRF: Add support for Action 0x10. This also required an extra pre-stage (before initialize and activation) to scan the GRF file for GOTO labels. Big thanks for peter1138 for the guidance and answers, as well as parts of the code.
authorDarkvater
Sat, 15 Apr 2006 21:27:59 +0000
changeset 3561 2f67415d44aa
parent 3560 b2fcf1898eec
child 3562 83a833bbe9ac
(svn r4439) - NewGRF: Add support for Action 0x10. This also required an extra pre-stage (before initialize and activation) to scan the GRF file for GOTO labels. Big thanks for peter1138 for the guidance and answers, as well as parts of the code.
newgrf.c
newgrf.h
--- a/newgrf.c	Sat Apr 15 20:07:42 2006 +0000
+++ b/newgrf.c	Sat Apr 15 21:27:59 2006 +0000
@@ -38,7 +38,7 @@
 GRFFile *_first_grffile;
 static int _cur_spriteid;
 static int _cur_stage;
-static int _nfo_line;
+static uint32 _nfo_line;
 
 /* 32 * 8 = 256 flags. Apparently TTDPatch uses this many.. */
 static uint32 _ttdpatch_flags[8];
@@ -1849,6 +1849,8 @@
 	uint32 param_val = 0;
 	uint32 cond_val = 0;
 	bool result;
+	GRFLabel *label;
+	GRFLabel *choice = NULL;
 
 	check_length(len, 6, "SkipIf");
 	param = buf[1];
@@ -1947,6 +1949,30 @@
 	}
 
 	numsprites = grf_load_byte(&buf);
+
+	/* numsprites can be a GOTO label if it has been defined in the GRF
+	 * file. The jump will always be the first matching label that follows
+	 * the current nfo_line. If no matching label is found, the first matching
+	 * label in the file is used. */
+	for (label = _cur_grffile->label; label != NULL; label = label->next) {
+		if (label->label != numsprites) continue;
+
+		/* Remember a goto before the current line */
+		if (choice == NULL) choice = label;
+		/* If we find a label here, this is definitely good */
+		if (label->nfo_line > _nfo_line) {
+			choice = label;
+			break;
+		}
+	}
+
+	if (choice != NULL) {
+		grfmsg(GMS_NOTICE, "Jumping to label 0x%0X at line %d, test was true.", choice->label, choice->nfo_line);
+		FioSeekTo(choice->pos, SEEK_SET);
+		_nfo_line = choice->nfo_line;
+		return;
+	}
+
 	grfmsg(GMS_NOTICE, "Skipping %d sprites, test was true.", numsprites);
 	_skip_sprites = numsprites;
 	if (_skip_sprites == 0) {
@@ -2065,6 +2091,12 @@
 	/* <0C> [<ignored...>]
 	 *
 	 * V ignored       Anything following the 0C is ignored */
+
+	static char comment[256];
+	if (len == 1) return;
+
+	ttd_strlcpy(comment, buf + 1, minu(sizeof(comment), len));
+	grfmsg(GMS_NOTICE, "GRFComment: %s", comment);
 }
 
 /* Action 0x0D */
@@ -2278,6 +2310,36 @@
 	}
 }
 
+static void DefineGotoLabel(byte *buf, int len)
+{
+	/* <10> <label> [<comment>]
+	 *
+	 * B label      The label to define
+	 * V comment    Optional comment - ignored */
+
+	GRFLabel *label;
+
+	check_length(len, 1, "GRFLabel");
+	buf++; len--;
+
+	label = malloc(sizeof(*label));
+	label->label    = grf_load_byte(&buf);
+	label->nfo_line = _nfo_line;
+	label->pos      = FioGetPos();
+	label->next     = NULL;
+
+	/* Set up a linked list of goto targets which we will search in an Action 0x7/0x9 */
+	if (_cur_grffile->label == NULL) {
+		_cur_grffile->label = label;
+	} else {
+		/* Attach the label to the end of the list */
+		GRFLabel *l;
+		for (l = _cur_grffile->label; l->next != NULL; l = l->next);
+		l->next = label;
+	}
+
+	grfmsg(GMS_NOTICE, "DefineGotoLabel: GOTO target with label 0x%X", label->label);
+}
 
 static void InitializeGRFSpecial(void)
 {
@@ -2410,6 +2472,19 @@
 	AddTypeToEngines();
 }
 
+/** Reset all NewGRFData that was used only while processing data */
+static void ClearTemporaryNewGRFData(void)
+{
+	/* Clear the GOTO labels used for GRF processing */
+	GRFLabel *l;
+	for (l = _cur_grffile->label; l != NULL;) {
+		GRFLabel *l2 = l->next;
+		free(l);
+		l = l2;
+	}
+	_cur_grffile->label = NULL;
+}
+
 static void InitNewGRFFile(const char* filename, int sprite_offset)
 {
 	GRFFile *newfile;
@@ -2482,14 +2557,18 @@
 {
 	/* XXX: There is a difference between staged loading in TTDPatch and
 	 * here.  In TTDPatch, for some reason actions 1 and 2 are carried out
-	 * during stage 0, whilst action 3 is carried out during stage 1 (to
+	 * during stage 1, whilst action 3 is carried out during stage 2 (to
 	 * "resolve" cargo IDs... wtf). This is a little problem, because cargo
 	 * IDs are valid only within a given set (action 1) block, and may be
 	 * overwritten after action 3 associates them. But overwriting happens
 	 * in an earlier stage than associating, so...  We just process actions
-	 * 1 and 2 in stage 1 now, let's hope that won't get us into problems.
+	 * 1 and 2 in stage 2 now, let's hope that won't get us into problems.
 	 * --pasky */
-	uint32 action_mask = (stage == 0) ? 0x0001FB40 : 0x0001FFBF;
+	/* We need a pre-stage to set up GOTO labels of Action 0x10 because the grf
+	 * is not in memory and scanning the file every time would be too expensive.
+	 * In other stages we skip action 0x10 since it's already dealt with. */
+	static const uint32 action_mask[] = {0x10000, 0x0000FB40, 0x0000FFBF};
+
 	static const SpecialSpriteHandler handlers[] = {
 		/* 0x00 */ VehicleChangeInfo,
 		/* 0x01 */ NewSpriteSet,
@@ -2507,7 +2586,7 @@
 		/* 0x0D */ ParamSet,
 		/* 0x0E */ GRFInhibit,
 		/* 0x0F */ NULL, // TODO implement
-		/* 0x10 */ NULL  // TODO implement
+		/* 0x10 */ DefineGotoLabel,
 	};
 
 	byte* buf = malloc(num);
@@ -2520,7 +2599,7 @@
 
 	if (action >= lengthof(handlers)) {
 		DEBUG(grf, 7) ("Skipping unknown action 0x%02X", action);
-	} else if (!HASBIT(action_mask, action)) {
+	} else if (!HASBIT(action_mask[stage], action)) {
 		DEBUG(grf, 7) ("Skipping action 0x%02X in stage %d", action, stage);
 	} else if (handlers[action] == NULL) {
 		DEBUG(grf, 7) ("Skipping unsupported Action 0x%02X", action);
@@ -2548,7 +2627,7 @@
 	if (stage != 0) {
 		_cur_grffile = GetFileByFilename(filename);
 		if (_cur_grffile == NULL) error("File ``%s'' lost in cache.\n", filename);
-		if (!(_cur_grffile->flags & 0x0001)) return;
+		if (stage > 1 && !(_cur_grffile->flags & 0x0001)) return;
 	}
 
 	FioOpenFile(file_index, filename);
@@ -2624,7 +2703,7 @@
 	 * in each loading stage, (try to) open each file specified in the config
 	 * and load information from it. */
 	_custom_sprites_base = load_index;
-	for (stage = 0; stage < 2; stage++) {
+	for (stage = 0; stage <= 2; stage++) {
 		uint slot = file_index;
 		uint j;
 
@@ -2637,6 +2716,7 @@
 			}
 			if (stage == 0) InitNewGRFFile(_newgrf_files[j], _cur_spriteid);
 			LoadNewGRFFile(_newgrf_files[j], slot++, stage);
+			if (stage == 2) ClearTemporaryNewGRFData();
 			DEBUG(spritecache, 2) ("Currently %i sprites are loaded", load_index);
 		}
 	}
--- a/newgrf.h	Sat Apr 15 20:07:42 2006 +0000
+++ b/newgrf.h	Sat Apr 15 21:27:59 2006 +0000
@@ -6,6 +6,13 @@
 #include "sprite.h"
 #include "station.h"
 
+typedef struct GRFLabel {
+	byte label;
+	uint32 nfo_line;
+	uint32 pos;
+	struct GRFLabel *next;
+} GRFLabel;
+
 typedef struct GRFFile GRFFile;
 struct GRFFile {
 	char *filename;
@@ -39,6 +46,8 @@
 
 	uint32 param[0x80];
 	uint param_end; /// one more than the highest set parameter
+
+	GRFLabel *label; ///< Pointer to the first label. This is a linked list, not an array.
 };
 
 extern GRFFile *_first_grffile;