Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
9169 turbocat 1
/*
2
 * OpenTyrian: A modern cross-platform port of Tyrian
3
 * Copyright (C) 2007-2009  The OpenTyrian Development Team
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18
 */
19
#include "animlib.h"
20
 
21
#include "file.h"
22
#include "keyboard.h"
23
#include "network.h"
24
#include "nortsong.h"
25
#include "palette.h"
26
#include "sizebuf.h"
27
#include "video.h"
28
 
29
#include 
30
 
31
/*** Structs ***/
32
/* The actual header has a lot of fields that are basically useless to us since
33
 * we both set our own framerate and the format itself only allows for
34
 * 320x200x8.  Should a (nonexistent) ani be played that doesn't have the same
35
 * assumed values we are going to use, TOO BAD.  It'll just be treated as
36
 * corrupt in playback.
37
 */
38
#define PALETTE_OFFSET    0x100 // 128 + sizeof(header)
39
#define PAGEHEADER_OFFSET 0x500 // PALETTE_OFFSET + sizeof(palette)
40
#define ANIM_OFFSET   0x0B00    // PAGEHEADER_OFFSET + sizeof(largepageheader) * 256
41
#define ANI_PAGE_SIZE 0x10000   // 65536.
42
typedef struct anim_FileHeader_s
43
{
44
	unsigned int nlps;            /* Number of 'pages', max 256. */
45
	unsigned int nRecords;        /* Number of 'records', max 65535 */
46
} anim_FileHeader_t;
47
typedef struct anim_LargePageHeader_s
48
{
49
	unsigned int baseRecord;      /* The first record's number */
50
	unsigned int nRecords;        /* Number of records.  Supposedly there are bit flags but I saw no such code */
51
	unsigned int nBytes;	      /* Number of bytes used, excluding headers */
52
} anim_LargePageHeader_t;
53
 
54
 
55
/*** Globals ***/
56
Uint8 CurrentPageBuffer[65536];
57
anim_LargePageHeader_t PageHeader[256];
58
unsigned int CurrentPageRecordSizes[256];
59
 
60
anim_LargePageHeader_t CurrentPageHeader;
61
anim_FileHeader_t FileHeader;
62
 
63
unsigned int Curlpnum;
64
 
65
FILE * InFile;
66
 
67
 
68
/*** Function decs ***/
69
int JE_playRunSkipDump( Uint8 *, unsigned int );
70
void JE_closeAnim( void );
71
int JE_loadAnim( const char * );
72
int JE_renderFrame( unsigned int );
73
int JE_findPage ( unsigned int );
74
int JE_drawFrame( unsigned int );
75
int JE_loadPage( unsigned int );
76
 
77
/*** Implementation ***/
78
 
79
 
80
/* Loads the given page into memory.
81
 *
82
 * Returns  0 on success or nonzero on failure (bad data)
83
 */
84
int JE_loadPage( unsigned int pagenumber )
85
{
86
	unsigned int i, pageSize;
87
 
88
 
89
	if (Curlpnum == pagenumber) { return(0); } /* Already loaded */
90
	Curlpnum = pagenumber;
91
 
92
	/* We need to seek to the page and load it into our buffer.
93
	 * Pages have a fixed size of 0x10000; any left over space is padded
94
	 * unless it's the end of the file.
95
	 *
96
	 * Pages repeat their headers for some reason.  They then have two bytes of
97
	 * padding folowed by a word for every record.  THEN the data starts.
98
	 */
99
	fseek(InFile, ANIM_OFFSET + (pagenumber * ANI_PAGE_SIZE), SEEK_SET);
100
	efread(&CurrentPageHeader.baseRecord, 2, 1, InFile);
101
	efread(&CurrentPageHeader.nRecords,   2, 1, InFile);
102
	efread(&CurrentPageHeader.nBytes,     2, 1, InFile);
103
 
104
	fseek(InFile, 2, SEEK_CUR);
105
	for (i = 0; i < CurrentPageHeader.nRecords; i++)
106
	{
107
		efread(&CurrentPageRecordSizes[i], 2, 1, InFile);
108
	}
109
 
110
	/* What remains is the 'compressed' data */
111
	efread(CurrentPageBuffer, 1, CurrentPageHeader.nBytes, InFile);
112
 
113
	/* Okay, we've succeeded in all our IO checks.  Now, make sure the
114
	 * headers aren't lying or damaged or something.
115
	 */
116
	pageSize = 0;
117
	for (i = 0; i < CurrentPageHeader.nRecords; i++)
118
	{
119
		pageSize += CurrentPageRecordSizes[i];
120
	}
121
 
122
	if(pageSize != CurrentPageHeader.nBytes) { return(-1); }
123
 
124
	/* So far, so good */
125
	return(0);
126
}
127
 
128
int JE_drawFrame( unsigned int framenumber )
129
{
130
	int ret;
131
 
132
 
133
	ret = JE_loadPage(framenumber);
134
	if (ret) { return(ret); }
135
 
136
	ret = JE_renderFrame (framenumber);
137
	if (ret) { return(ret); }
138
 
139
	return(0);
140
}
141
 
142
int JE_findPage( unsigned int framenumber )
143
{
144
	unsigned int i;
145
 
146
 
147
	for (i = 0; i < FileHeader.nlps; i++)
148
	{
149
		if (PageHeader[i].baseRecord <= framenumber
150
		 && PageHeader[i].baseRecord + PageHeader[i].nRecords > framenumber)
151
		{
152
			return(i);
153
		}
154
	}
155
 
156
	return(-1); /* Did not find */
157
}
158
 
159
int JE_renderFrame( unsigned int framenumber )
160
{
161
	unsigned int i, offset, destframe;
162
 
163
 
164
	destframe = framenumber - CurrentPageHeader.baseRecord;
165
 
166
	offset = 0;
167
	for (i = 0; i < destframe; i++)
168
	{
169
		offset += CurrentPageRecordSizes[i];
170
	}
171
 
172
	return (JE_playRunSkipDump(CurrentPageBuffer + offset + 4, CurrentPageRecordSizes[destframe] - 4));
173
}
174
 
175
void JE_playAnim( const char *animfile, JE_byte startingframe, JE_byte speed )
176
{
177
	unsigned int i;
178
	int pageNum;
179
 
180
	if (JE_loadAnim(animfile) != 0)
181
	{
182
		return; /* Failed to open or process file */
183
	}
184
 
185
	/* Blank screen */
186
	JE_clr256(VGAScreen);
187
	JE_showVGA();
188
 
189
 
190
	/* re FileHeader.nRecords-1: It's -1 in the pascal too.
191
	 * The final frame is a delta of the first, and we don't need that.
192
	 * We could also, if we ever ended up needing to loop anis, check
193
	 * the bools in the header to see if we should render the last
194
	 * frame.  But that's never going to be encessary :)
195
	 */
196
    for (i = startingframe; i < FileHeader.nRecords-1; i++)
197
    {
198
    	/* Handle boring crap */
199
    	setjasondelay(speed);
200
 
201
		/* Load required frame.  The loading function is smart enough to not re-load an already loaded frame */
202
		pageNum = JE_findPage(i);
203
		if(pageNum == -1) { break; }
204
		if (JE_loadPage(pageNum) != 0) { break; }
205
 
206
		/* render frame. */
207
    	if (JE_renderFrame(i) != 0) { break; }
208
    	JE_showVGA();
209
 
210
 
211
		/* Return early if user presses a key */
212
		service_SDL_events(true);
213
		if (newkey)
214
		{
215
			break;
216
		}
217
 
218
		/* Wait until we need the next frame */
219
		NETWORK_KEEP_ALIVE();
220
		wait_delay();
221
    }
222
 
223
	JE_closeAnim();
224
}
225
 
226
/* loadAnim opens the file and loads data from it into the header structs.
227
 * It should take care to clean up after itself should an error occur.
228
 */
229
int JE_loadAnim( const char *filename )
230
{
231
	unsigned int i, fileSize;
232
	char temp[4];
233
 
234
 
235
	Curlpnum = -1;
236
	InFile = dir_fopen(data_dir(), filename, "rb");
237
	if(InFile == NULL)
238
	{
239
		return(-1);
240
	}
241
 
242
	fileSize = ftell_eof(InFile);
243
	if(fileSize < ANIM_OFFSET)
244
	{
245
		/* We don't know the exact size our file should be yet,
246
		 * but we do know it should be way more than this */
247
		fclose(InFile);
248
		return(-1);
249
	}
250
 
251
	/* Read in the header.  The header is 256 bytes long or so,
252
	 * but that includes a lot of padding as well as several
253
	 * vars we really don't care about.  We shall check the ID and extract
254
	 * the handful of vars we care about.  Every value in the header that
255
	 * is constant will be ignored.
256
	 */
257
 
258
	efread(&temp, 1, 4, InFile); /* The ID, should equal "LPF " */
259
	fseek(InFile, 2, SEEK_CUR); /* skip over this word */
260
	efread(&FileHeader.nlps, 2, 1, InFile); /* Number of pages */
261
	efread(&FileHeader.nRecords, 4, 1, InFile); /* Number of records */
262
 
263
	if (memcmp(temp, "LPF ", 4) != 0
264
	 || FileHeader.nlps == 0  || FileHeader.nRecords == 0
265
	 || FileHeader.nlps > 256 || FileHeader.nRecords > 65535)
266
	{
267
		fclose(InFile);
268
		return(-1);
269
	}
270
 
271
	/* Read in headers */
272
	fseek(InFile, PAGEHEADER_OFFSET, SEEK_SET);
273
	for (i = 0; i < FileHeader.nlps; i++)
274
	{
275
		efread(&PageHeader[i].baseRecord, 2, 1, InFile);
276
		efread(&PageHeader[i].nRecords,   2, 1, InFile);
277
		efread(&PageHeader[i].nBytes,     2, 1, InFile);
278
	}
279
 
280
 
281
	/* Now we have enough information to calculate the 'expected' file size.
282
	 * Our calculation SHOULD be equal to fileSize, but we won't begrudge
283
	 * padding */
284
	if (fileSize < (FileHeader.nlps-1) * ANI_PAGE_SIZE + ANIM_OFFSET
285
	  + PageHeader[FileHeader.nlps-1].nBytes
286
	  + PageHeader[FileHeader.nlps-1].nRecords * 2 + 8)
287
	{
288
		fclose(InFile);
289
		return(-1);
290
	}
291
 
292
 
293
	/* Now read in the palette. */
294
	fseek(InFile, PALETTE_OFFSET, SEEK_SET);
295
	for (i = 0; i < 256; i++)
296
	{
297
		efread(&colors[i].b,      1, 1, InFile);
298
		efread(&colors[i].g,      1, 1, InFile);
299
		efread(&colors[i].r,      1, 1, InFile);
300
		efread(&colors[i].unused, 1, 1, InFile);
301
	}
302
	set_palette(colors, 0, 255);
303
 
304
	/* Whew!  That was hard.  Let's go grab some beers! */
305
	return(0);
306
}
307
 
308
void JE_closeAnim( void )
309
{
310
	fclose(InFile);
311
}
312
 
313
/* RunSkipDump decompresses the video.  There are three operations, run, skip,
314
 * and dump.  They can be used in either byte or word variations, making six
315
 * possible actions, and there's a seventh 'stop' action, which looks
316
 * like 0x80 0x00 0x00.
317
 *
318
 * Run is a memset.
319
 * Dump is a memcpy.
320
 * Skip leaves the old data intact and simply increments the pointers.
321
 *
322
 * returns 0 on success or 1 if decompressing failed.  Failure to decompress
323
 * indicates a broken or malicious file; playback should terminate.
324
 */
325
int JE_playRunSkipDump( Uint8 *incomingBuffer, unsigned int IncomingBufferLength )
326
{
327
	sizebuf_t Buffer_IN, Buffer_OUT;
328
	sizebuf_t * pBuffer_IN = &Buffer_IN, * pBuffer_OUT = &Buffer_OUT;
329
 
330
	#define ANI_SHORT_RLE  0x00
331
	#define ANI_SHORT_SKIP 0x80
332
	#define ANI_LONG_OP    0x80
333
	#define ANI_LONG_COPY_OR_RLE  0x8000
334
	#define ANI_LONG_RLE   0x4000
335
	#define ANI_STOP       0x0000
336
 
337
	SZ_Init(pBuffer_IN,  incomingBuffer,    IncomingBufferLength);
338
	SZ_Init(pBuffer_OUT, VGAScreen->pixels, VGAScreen->h * VGAScreen->pitch);
339
 
340
 
341
	/* 320x200 is the only supported format.
342
	 * Assert is here as a hint should our screen size ever changes.
343
	 * As for how to decompress to the wrong screen size... */
344
	assert(VGAScreen->h * VGAScreen->pitch == 320 * 200);
345
 
346
 
347
	while (1)
348
	{
349
		/* Get one byte.  This byte may have flags that tell us more */
350
		unsigned int opcode = MSG_ReadByte(pBuffer_IN);
351
 
352
		/* Before we continue, check the error states/
353
		 * We should *probably* check these after every read and write, but
354
		 * I've rigged it so that the buffers will never go out of bounds.
355
		 * So we can afford to be lazy; if the buffer overflows below it will
356
		 * silently fail its writes and we'll catch the failure on our next
357
		 * run through the loop.  A failure means we should be
358
		 * leaving ANYWAY.  The contents of our buffers doesn't matter.
359
		 */
360
		if (SZ_Error(pBuffer_IN) || SZ_Error(pBuffer_OUT))
361
		{
362
			return(-1);
363
		}
364
 
365
		/* Divide into 'short' and 'long' */
366
		if (opcode == ANI_LONG_OP) /* long ops */
367
		{
368
			opcode = MSG_ReadWord(pBuffer_IN);
369
 
370
			if (opcode == ANI_STOP) /* We are done decompressing.  Leave */
371
			{
372
				break;
373
			}
374
			else if (!(opcode & ANI_LONG_COPY_OR_RLE)) /* If it's not those two, it's a skip */
375
			{
376
				unsigned int count = opcode;
377
				SZ_Seek(pBuffer_OUT, count, SEEK_CUR);
378
			}
379
			else /* Now things get a bit more interesting... */
380
			{
381
				opcode &= ~ANI_LONG_COPY_OR_RLE; /* Clear that flag */
382
 
383
				if (opcode & ANI_LONG_RLE) /* RLE */
384
				{
385
					unsigned int count = opcode & ~ANI_LONG_RLE; /* Clear flag */
386
 
387
					/* Extract another byte */
388
					unsigned int value = MSG_ReadByte(pBuffer_IN);
389
 
390
					/* The actual run */
391
					SZ_Memset(pBuffer_OUT, value, count);
392
				}
393
				else
394
				{ /* Long copy */
395
					unsigned int count = opcode;
396
 
397
					/* Copy */
398
					SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count);
399
				}
400
			}
401
		} /* End of long ops */
402
		else /* short ops */
403
		{
404
			if (opcode & ANI_SHORT_SKIP) /* Short skip, move pointer only */
405
			{
406
				unsigned int count = opcode & ~ANI_SHORT_SKIP; /* clear flag to get count */
407
				SZ_Seek(pBuffer_OUT, count, SEEK_CUR);
408
			}
409
			else if (opcode == ANI_SHORT_RLE) /* Short RLE, memset the destination */
410
			{
411
				/* Extract a few more bytes */
412
				unsigned int count = MSG_ReadByte(pBuffer_IN);
413
				unsigned int value = MSG_ReadByte(pBuffer_IN);
414
 
415
				/* Run */
416
				SZ_Memset(pBuffer_OUT, value, count);
417
			}
418
			else /* Short copy, memcpy from src to dest. */
419
			{
420
				unsigned int count = opcode;
421
 
422
				/* Dump */
423
				SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count);
424
			}
425
		} /* End of short ops */
426
	}
427
 
428
	/* And that's that */
429
	return(0);
430
}
431