-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathporting.txt
375 lines (257 loc) · 13.2 KB
/
porting.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
Level 9 interpreter
Version 5.2
This guide is by David Kinder.
Level9 has already been compiled on Windows, MS-DOS, Apple Mac, Amiga, Acorn
and Unix systems, so it should be quite easy to get it working on any other
modern computer.
The first thing that must be done is to check the typedefs in level9.h.
The typedefs must have the following properties:
L9BYTE unsigned 8 bit quantity
L9UINT16 unsigned 16 bit quantity
L9UINT32 unsigned 32 bit quantity
L9BOOL quantity capable of holding the values
TRUE (1) and FALSE (0)
Also the define MAX_PATH (the maximum length of the full pathname of a file)
must be set, e.g.
#define MAX_PATH 256
If graphics are not supported defining NO_SCAN_GRAPHICS will stop the
intialization code from looking for graphics data, which may take a noticeable
length of time on slower computers.
It is required that several os_ functions be written for your system. Given
below is a guide to these functions, and a very simple interface is included
in generic.c.
void os_printchar(char c)
os_printchar() prints a character to the output. The interface
can either buffer this character or print it immediately, but
if buffering is used then the characters must all be sent to the
output when the interpreter calls os_flush(). A paragraph of
text is output as one long stream of characters, without line
breaks, so the interface must provide its own word wrapping and
any other features that are desired, such as justification or a
[More] prompt. The carriage return character is always '\r',
rather than '\n'.
L9BOOL os_input(char* ibuff, int size)
os_input() reads a line of text from the user, usually to accept
the next command to be sent to the game. The text input must be
stored in ibuff with a terminating zero, and be no longer than
size characters. Normally os_input() should return TRUE, but may
return FALSE to cause the entire input so far to be discarded.
The reason for doing so is discussed in the section at the end
on allowing the interpreter to load a new game without exiting.
char os_readchar(L9UINT32 millis)
os_readchar() looks to see if a key has been pressed if one has,
returns the character to the interpreter immediately. If no key
has been pressed the interpreter should wait for a key for at
least the number of milliseconds given in the argument. If after
this period no key has been pressed, 0 should be returned. This
is most commonly used when a game is exited, causing it to print
"Press SPACE to play again" and then call os_readchar().
L9BOOL os_stoplist(void)
Called during dictionary listing. If true is returned (typically
because the user has pressed a key) then the listing is stopped.
This routine should return immediately, without waiting. If this
is not possible then FALSE should be returned.
void os_flush(void)
If the calls to os_printchar() are being buffered by the
interface then the buffered text must be printed when os_flush()
is called.
L9BOOL os_save_file(L9BYTE* Ptr, int Bytes)
os_save_file() should prompt the user in some way (with either
text or a file requester) for a filename to save the area of
memory of size Bytes pointed to by Ptr. TRUE or FALSE should be
returned depending on whether or not the operation was successful.
L9BOOL os_load_file(L9BYTE* Ptr, int* Bytes, int Max)
os_load_file() should prompt the user for the name of a file to
load. At most Max bytes should be loaded into the memory pointed
to by Ptr, and the number of bytes read should be placed into the
variable pointed to by Bytes.
L9BOOL os_get_game_file(char* NewName, int Size)
os_get_game_file() should prompt the user for a new game file, to
be stored in NewName, which can take a maximum name of Size
characters. When this function is called the NewName array
contains the name of the currently loaded game, which can be used
to derive a name to prompt the user with.
This is used by at least the Adrian Mole games, which load in the
next part of the game after the part currently being played has
been completed. These games were originally written for tape-based
systems where the call was simply "load the next game from the
tape".
void os_set_filenumber(char* NewName, int Size, int n)
os_set_filename() is for multi-part games originally written for
disk-based systems, which used game filenames such as
gamedat1.dat
gamedat2.dat
etc. The routine should take the full filename in NewName (of
maximum size Size) and modify it to reflect the number n, e.g.
os_set_filename("gamedat1.dat",2) should leave "gamedat2.dat"
in NewName.
FILE* os_open_script_file(void)
os_open_script_file() should prompt the user for the name of a
script file, from which input will be read until the end of the
script file is reached, and should return a pointer to the opened
script file, or NULL. This function is called in response to
the player entering the "#play" meta-command.
void os_graphics(int mode)
Called when graphics are turned on or off, either by the game or
by the user entering "graphics" or "text" as a command. If mode
is 0 graphics should be turned off. If mode is 1 then line drawn
graphics will follow, so graphics should be turned on. If mode is
2 then bitmap graphics will follow, so graphics should be turned
on, provided that appropriate bitmap graphics files are available
(This can be determined by calling DetectBitmaps(), which is
discussed below.). After an os_graphics(0) call all the other
graphics functions should do nothing.
Typically, if mode is not 0 the code will allocate some suitable
bitmap for drawing graphics into. For line drawn graphics, to
determine the size of the bitmap the code should call
GetPictureSize(). The graphics routines should draw in a bitmap of
the size returned by this function, and then scale the bitmap
appropriately for display. If instead the graphics code tries to
scale the co-ordinates passed to os_drawline() and os_fill() then
problems occur with fill colours "leaking" into other areas of the
picture. The values returned by GetPictureSize() will not change
unless a new game is loaded.
The graphics bitmap for line drawn graphics is always 4 colour.
The 4 colours are chosen from a possible 8 by calls to
os_setcolour (see below). Note that a call to os_setcolour() must
affect the colour of pixels already drawn on the bitmap. For
example, suppose a pixel in the bitmap is set to the first colour
in the palette during drawing, which at that moment is red. If
later the first colour in the palette is set to blue, at the end
the pixel should be shown blue.
In order to actually draw graphics, the input routines os_input()
and os_readchar() should call RunGraphics(). This is discussed
further below.
void os_cleargraphics(void)
Clear the current graphics bitmap by filling the entire bitmap
with colour 0.
void os_setcolour(int colour, int index)
Set the given colour in the graphics bitmap's palette to the
colour at the given index in the interpreter's table of colours.
The actual table of colours in the interpreters provided by
Level 9 vary across different machines. An acceptable palette
that matches reasonably closely to the Amiga releases is as
follows (all colours are 8 bit R,G,B):
0x00,0x00,0x00 (black)
0xFF,0x00,0x00 (red)
0x30,0xE8,0x30 (green)
0xFF,0xFF,0x00 (yellow)
0x00,0x00,0xFF (blue)
0xA0,0x68,0x00 (brown)
0x00,0xFF,0xFF (cyan)
0xFF,0xFF,0xFF (white)
void os_drawline(int x1, int y1, int x2, int y2, int colour1, int colour2)
Draw a line on the graphics bitmap between (x1,y1) and (x2,y2).
Note that either point may lie outside of the bitmap, and that it
is the responsibility of the routine to clip to the appropriate
co-ordinates.
For each point on the line, if the colour at that point is equal
to colour2 the pixel's colour should be changed to colour1, else
it should not be modified.
void os_fill(int x, int y, int colour1, int colour2)
If the pixel's colour at (x,y) is equal to colour2, fill the
region containing (x,y) with colour1. The boundaries of the
region are defined as those areas of the bitmap with a colour
other than colour2.
void os_show_bitmap(int pic, int x, int y)
Show the bitmap given by the number pic at the co-ordinates
(x,y).
Note that the game can request the same picture several times
in a row: it is a good idea for ports to record the last picture
number and check it against any new requests.
The interpreter source code provides a decoder that understands
most Level 9 bitmap formats. The decoder is accessed by calling
DecodeBitmap(), which is discussed below.
L9BOOL os_find_file(char* NewName)
Return whether the given file exists or not. On case sensitive
file systems, the interpreter should check whether the file
exists as all uppercase and all lowercase. If it does, the
routine should return TRUE, with the string pointed to by
NewName changed to match the file name as it appears in the
file system.
int main(int argc, char** argv)
You must provide your own main() entry point for the program.
The simplest such main() is given in generic.c, which just calls
LoadGame() and then sits in a loop calling RunGame(). These
functions are discussed below.
The interpreter provides several functions to be called by the interface
code. These are:
L9BOOL LoadGame(char* filename, char* picname)
LoadGame() attempts to load filename and then searches it for
a valid Level 9 game. If it is successful TRUE is returned, else
FALSE. The previous game in memory will be overwritten if the
file filename can be loaded, even if it does not contain a Level 9
game, so even if LoadGame() returns FALSE it must be assumed that
the game memory has changed.
The second argument is the name of the file containing picture
data, and may be NULL. Ports should usually ask the user for just
the filename and derive picname from it in some way. The
recommended approach is to first try the filename with an extension
of ".pic" and then try replacing the filename with "picture.dat".
L9BOOL RunGame(void)
If LoadGame() has been successful, RunGame() can be called to run
the Level 9 game. Each call to RunGame() executes a single opcode
of the game. In pre-emptive multitasking systems or systems without
any multitasking it is enough to sit in a loop calling RunGame(),
e.g.
while (RunGame());
RunGame() returns TRUE if an opcode code was executed and FALSE if
the game is stopped, either by an error or by a call to StopGame().
void StopGame(void)
StopGame() stops the current game from playing.
void RestoreGame(char *inFile)
RestoreGame() attempts to restore the currently running game to
the position stored in the inFile saved game file. This gives
interface code a means to restore a game position.
void FreeMemory(void)
FreeMemory() frees memory used to store the game. This routine
should be called when exiting the interpreter.
void GetPictureSize(int* width, int* height)
Returns the width and height of the bitmap that graphics should
be drawn into. This is constant for any particular game.
L9BOOL RunGraphics(void)
Runs an opcode of the graphics routines. If a graphics opcode was
run TRUE is returned, otherwise FALSE.
The simplest way to get graphics to display is to add a loop to
repeatedly call RunGraphics() to os_input() and os_readchar():
while (RunGraphics());
/* Now draw graphics bitmap on display... */
Optionally, the code can provide a more "atmospheric" recreation
of the games by drawing the graphics slowly, as was the case on
the old 8-bit computers. This is achieved by calling RunGraphics()
several times then waiting for a while before calling it again.
Note that when waiting the code should still respond to user
input.
BitmapType DetectBitmaps(char* dir)
Given a directory, returns the type of bitmap picture files in it,
or NO_BITMAPS if there are no bitmaps.
This function is only available if the preprocessor symbol
BITMAP_DECODER is defined.
Bitmap* DecodeBitmap(char* dir, BitmapType type, int num, int x, int y)
This function loads and decodes the specified bitmap, returning
a Bitmap structure. The structure contains the width and height
of the bitmap, a palette of up to 32 colours used in the bitmap,
and the actual data as an array of indexes into the palette.
This function is only available if the preprocessor symbol
BITMAP_DECODER is defined.
One more complex feature of the interpreter is that a new Level 9 game can
be loaded without exiting and restarting the interpreter. This is of use
in a windowing environment. In this case, both main() and the code that
catches a "New Game" menu item should call a routine such as the example
new_game() below. This ensures that each new game does not use up more and
more of the interpreter's stack.
int newgame(char* game_name)
{
static int playing = FALSE;
if (LoadGame(game,NULL))
{
if (playing)
return -1;
playing = TRUE;
while (RunGame());
playing = FALSE;
}
else
warn("Unable to load game");
return -1;
}