1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 
24 #include "common/debug.h"
25 #include "common/endian.h"
26 #include "common/file.h"
27 #include "common/textconsole.h"
28 #include "common/translation.h"
29 #include "sky/compact.h"
30 #include "gui/message.h"
31 #include <stddef.h>	// for ptrdiff_t
32 
33 namespace Sky {
34 
35 #define	SKY_CPT_SIZE	419427
36 
37 #define OFFS(type,item) ((uint32)(((ptrdiff_t)(&((type *)42)->item))-42))
38 #define MK32(type,item) OFFS(type, item),0,0,0
39 #define MK16(type,item) OFFS(type, item),0
40 #define MK32_A5(type, item) MK32(type, item[0]), MK32(type, item[1]), \
41 	MK32(type, item[2]), MK32(type, item[3]), MK32(type, item[4])
42 
43 static const uint32 compactOffsets[] = {
44 	MK16(Compact, logic),
45 	MK16(Compact, status),
46 	MK16(Compact, sync),
47 	MK16(Compact, screen),
48 	MK16(Compact, place),
49 	MK32(Compact, getToTableId),
50 	MK16(Compact, xcood),
51 	MK16(Compact, ycood),
52 	MK16(Compact, frame),
53 	MK16(Compact, cursorText),
54 	MK16(Compact, mouseOn),
55 	MK16(Compact, mouseOff),
56 	MK16(Compact, mouseClick),
57 	MK16(Compact, mouseRelX),
58 	MK16(Compact, mouseRelY),
59 	MK16(Compact, mouseSizeX),
60 	MK16(Compact, mouseSizeY),
61 	MK16(Compact, actionScript),
62 	MK16(Compact, upFlag),
63 	MK16(Compact, downFlag),
64 	MK16(Compact, getToFlag),
65 	MK16(Compact, flag),
66 	MK16(Compact, mood),
67 	MK32(Compact, grafixProgId),
68 	MK16(Compact, offset),
69 	MK16(Compact, mode),
70 	MK16(Compact, baseSub),
71 	MK16(Compact, baseSub_off),
72 	MK16(Compact, actionSub),
73 	MK16(Compact, actionSub_off),
74 	MK16(Compact, getToSub),
75 	MK16(Compact, getToSub_off),
76 	MK16(Compact, extraSub),
77 	MK16(Compact, extraSub_off),
78 	MK16(Compact, dir),
79 	MK16(Compact, stopScript),
80 	MK16(Compact, miniBump),
81 	MK16(Compact, leaving),
82 	MK16(Compact, atWatch),
83 	MK16(Compact, atWas),
84 	MK16(Compact, alt),
85 	MK16(Compact, request),
86 	MK16(Compact, spWidth_xx),
87 	MK16(Compact, spColor),
88 	MK16(Compact, spTextId),
89 	MK16(Compact, spTime),
90 	MK16(Compact, arAnimIndex),
91 	MK32(Compact, turnProgId),
92 	MK16(Compact, waitingFor),
93 	MK16(Compact, arTargetX),
94 	MK16(Compact, arTargetY),
95 	MK32(Compact, animScratchId),
96 	MK16(Compact, megaSet),
97 };
98 
99 static const uint32 megaSetOffsets[] = {
100 	MK16(MegaSet, gridWidth),
101 	MK16(MegaSet, colOffset),
102 	MK16(MegaSet, colWidth),
103 	MK16(MegaSet, lastChr),
104 	MK32(MegaSet, animUpId),
105 	MK32(MegaSet, animDownId),
106 	MK32(MegaSet, animLeftId),
107 	MK32(MegaSet, animRightId),
108 	MK32(MegaSet, standUpId),
109 	MK32(MegaSet, standDownId),
110 	MK32(MegaSet, standLeftId),
111 	MK32(MegaSet, standRightId),
112 	MK32(MegaSet, standTalkId),
113 };
114 
115 static const uint32 turnTableOffsets[] = {
116 	MK32_A5(TurnTable, turnTableUp),
117 	MK32_A5(TurnTable, turnTableDown),
118 	MK32_A5(TurnTable, turnTableLeft),
119 	MK32_A5(TurnTable, turnTableRight),
120 	MK32_A5(TurnTable, turnTableTalk),
121 };
122 
123 #define COMPACT_SIZE (sizeof(compactOffsets)/sizeof(uint32))
124 #define MEGASET_SIZE (sizeof(megaSetOffsets)/sizeof(uint32))
125 #define TURNTABLE_SIZE (sizeof(turnTableOffsets)/sizeof(uint32))
126 
SkyCompact()127 SkyCompact::SkyCompact() {
128 	_cptFile = new Common::File();
129 	Common::String filename = "sky.cpt";
130 	if (!_cptFile->open(filename.c_str())) {
131                 Common::String msg = Common::String::format(_("Unable to locate the '%s' engine data file."), filename.c_str());
132                 GUIErrorMessage(msg);
133                 error("%s", msg.c_str());
134 	}
135 
136 	uint16 fileVersion = _cptFile->readUint16LE();
137 	if (fileVersion != 0)
138 		error("unknown \"sky.cpt\" version");
139 
140 	if (SKY_CPT_SIZE != _cptFile->size()) {
141 		GUI::MessageDialog dialog(_("The \"sky.cpt\" engine data file has an incorrect size."), _("OK"), NULL);
142 		dialog.runModal();
143 		error("Incorrect sky.cpt size (%d, expected: %d)", _cptFile->size(), SKY_CPT_SIZE);
144 	}
145 
146 	// set the necessary data structs up...
147 	_numDataLists = _cptFile->readUint16LE();
148 	_cptNames	  = (char***)malloc(_numDataLists * sizeof(char**));
149 	_dataListLen  = (uint16 *)malloc(_numDataLists * sizeof(uint16));
150 	_cptSizes	  = (uint16 **)malloc(_numDataLists * sizeof(uint16 *));
151 	_cptTypes	  = (uint16 **)malloc(_numDataLists * sizeof(uint16 *));
152 	_compacts	  = (Compact***)malloc(_numDataLists * sizeof(Compact**));
153 
154 	for (int i = 0; i < _numDataLists; i++) {
155 		_dataListLen[i] = _cptFile->readUint16LE();
156 		_cptNames[i] = (char**)malloc(_dataListLen[i] * sizeof(char *));
157 		_cptSizes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
158 		_cptTypes[i] = (uint16 *)malloc(_dataListLen[i] * sizeof(uint16));
159 		_compacts[i] = (Compact**)malloc(_dataListLen[i] * sizeof(Compact *));
160 	}
161 
162 	uint32 rawSize = _cptFile->readUint32LE() * sizeof(uint16);
163 	uint16 *rawPos = _rawBuf = (uint16 *)malloc(rawSize);
164 
165 	uint32 srcSize = _cptFile->readUint32LE() * sizeof(uint16);
166 	uint16 *srcBuf = (uint16 *)malloc(srcSize);
167 	uint16 *srcPos = srcBuf;
168 	_cptFile->read(srcBuf, srcSize);
169 
170 	uint32 asciiSize = _cptFile->readUint32LE();
171 	char *asciiPos = _asciiBuf = (char *)malloc(asciiSize);
172 	_cptFile->read(_asciiBuf, asciiSize);
173 
174 	// and fill them with the compact data
175 	for (uint32 lcnt = 0; lcnt < _numDataLists; lcnt++) {
176 		for (uint32 ecnt = 0; ecnt < _dataListLen[lcnt]; ecnt++) {
177 			_cptSizes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
178 			if (_cptSizes[lcnt][ecnt]) {
179 				_cptTypes[lcnt][ecnt] = READ_LE_UINT16(srcPos++);
180 				_compacts[lcnt][ecnt] = (Compact *)rawPos;
181 				_cptNames[lcnt][ecnt] = asciiPos;
182 				asciiPos += strlen(asciiPos) + 1;
183 
184 				for (uint16 elemCnt = 0; elemCnt < _cptSizes[lcnt][ecnt]; elemCnt++)
185 					*rawPos++ = READ_LE_UINT16(srcPos++);
186 			} else {
187 				_cptTypes[lcnt][ecnt] = 0;
188 				_compacts[lcnt][ecnt] = NULL;
189 				_cptNames[lcnt][ecnt] = NULL;
190 			}
191 		}
192 	}
193 	free(srcBuf);
194 
195 	uint16 numDlincs = _cptFile->readUint16LE();
196 	uint16 *dlincBuf = (uint16 *)malloc(numDlincs * 2 * sizeof(uint16));
197 	uint16 *dlincPos = dlincBuf;
198 	_cptFile->read(dlincBuf, numDlincs * 2 * sizeof(uint16));
199 	// these compacts don't actually exist but only point to other ones...
200 	uint16 cnt;
201 	for (cnt = 0; cnt < numDlincs; cnt++) {
202 		uint16 dlincId = READ_LE_UINT16(dlincPos++);
203 		uint16 destId = READ_LE_UINT16(dlincPos++);
204 		assert(((dlincId >> 12) < _numDataLists) && ((dlincId & 0xFFF) < _dataListLen[dlincId >> 12]) && (_compacts[dlincId >> 12][dlincId & 0xFFF] == NULL));
205 		_compacts[dlincId >> 12][dlincId & 0xFFF] = _compacts[destId >> 12][destId & 0xFFF];
206 
207 		assert(_cptNames[dlincId >> 12][dlincId & 0xFFF] == NULL);
208 		_cptNames[dlincId >> 12][dlincId & 0xFFF] = asciiPos;
209 		asciiPos += strlen(asciiPos) + 1;
210 	}
211 	free(dlincBuf);
212 
213 	// if this is v0.0288, parse this diff data
214 	uint16 numDiffs = _cptFile->readUint16LE();
215 	uint16 diffSize = _cptFile->readUint16LE();
216 	uint16 *diffBuf = (uint16 *)malloc(diffSize * sizeof(uint16));
217 	_cptFile->read(diffBuf, diffSize * sizeof(uint16));
218 	if (SkyEngine::_systemVars.gameVersion == 288) {
219 		uint16 *diffPos = diffBuf;
220 		for (cnt = 0; cnt < numDiffs; cnt++) {
221 			uint16 cptId = READ_LE_UINT16(diffPos++);
222 			uint16 *rawCpt = (uint16 *)fetchCpt(cptId);
223 			rawCpt += READ_LE_UINT16(diffPos++);
224 			uint16 len = READ_LE_UINT16(diffPos++);
225 			for (uint16 elemCnt = 0; elemCnt < len; elemCnt++)
226 				rawCpt[elemCnt] = READ_LE_UINT16(diffPos++);
227 		}
228 		assert(diffPos == (diffBuf + diffSize));
229 	}
230 	free(diffBuf);
231 
232 	// these are the IDs that have to be saved into savegame files.
233 	_numSaveIds = _cptFile->readUint16LE();
234 	_saveIds = (uint16 *)malloc(_numSaveIds * sizeof(uint16));
235 	_cptFile->read(_saveIds, _numSaveIds * sizeof(uint16));
236 	for (cnt = 0; cnt < _numSaveIds; cnt++)
237 		_saveIds[cnt] = FROM_LE_16(_saveIds[cnt]);
238 	_resetDataPos = _cptFile->pos();
239 
240 	checkAndFixOfficerBluntError();
241 }
242 
~SkyCompact()243 SkyCompact::~SkyCompact() {
244 	free(_rawBuf);
245 	free(_asciiBuf);
246 	free(_saveIds);
247 	for (int i = 0; i < _numDataLists; i++) {
248 		free(_cptNames[i]);
249 		free(_cptSizes[i]);
250 		free(_cptTypes[i]);
251 		free(_compacts[i]);
252 	}
253 	free(_cptNames);
254 	free(_dataListLen);
255 	free(_cptSizes);
256 	free(_cptTypes);
257 	free(_compacts);
258 	_cptFile->close();
259 	delete _cptFile;
260 }
261 
262 /* WORKAROUND for bug #2687:
263 	The first release of scummvm with externalized, binary compact data has one broken 16 bit reference.
264 	When talking to Officer Blunt on ground level while in a crouched position, the game enters an
265 	unfinishable state because Blunt jumps into the lake and can no longer be interacted with.
266 	This fixes the problem when playing with a broken sky.cpt */
267 #define SCUMMVM_BROKEN_TALK_INDEX 158
checkAndFixOfficerBluntError()268 void SkyCompact::checkAndFixOfficerBluntError() {
269 	// Retrieve the table with the animation ids to use for talking
270 	uint16 *talkTable = (uint16*)fetchCpt(CPT_TALK_TABLE_LIST);
271 	if (talkTable[SCUMMVM_BROKEN_TALK_INDEX] == ID_SC31_GUARD_TALK) {
272 		debug(1, "SKY.CPT with Officer Blunt bug encountered, fixing talk gfx.");
273 		talkTable[SCUMMVM_BROKEN_TALK_INDEX] = ID_SC31_GUARD_TALK2;
274 	}
275 }
276 
277 // needed for some workaround where the engine has to check if it's currently processing joey, for example
cptIsId(Compact * cpt,uint16 id)278 bool SkyCompact::cptIsId(Compact *cpt, uint16 id) {
279 	return (cpt == fetchCpt(id));
280 }
281 
fetchCpt(uint16 cptId)282 Compact *SkyCompact::fetchCpt(uint16 cptId) {
283 	if (cptId == 0xFFFF) // is this really still necessary?
284 		return NULL;
285 	assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
286 
287 	debug(8, "Loading Compact %s [%s] (%04X=%d,%d)", _cptNames[cptId >> 12][cptId & 0xFFF], nameForType(_cptTypes[cptId >> 12][cptId & 0xFFF]), cptId, cptId >> 12, cptId & 0xFFF);
288 
289 	return _compacts[cptId >> 12][cptId & 0xFFF];
290 }
291 
fetchCptInfo(uint16 cptId,uint16 * elems,uint16 * type,char * name)292 Compact *SkyCompact::fetchCptInfo(uint16 cptId, uint16 *elems, uint16 *type, char *name) {
293 	assert(((cptId >> 12) < _numDataLists) && ((cptId & 0xFFF) < _dataListLen[cptId >> 12]));
294 	if (elems)
295 		*elems = _cptSizes[cptId >> 12][cptId & 0xFFF];
296 	if (type)
297 		*type  = _cptTypes[cptId >> 12][cptId & 0xFFF];
298 	if (name) {
299 		if (_cptNames[cptId >> 12][cptId & 0xFFF] != NULL)
300 			strcpy(name, _cptNames[cptId >> 12][cptId & 0xFFF]);
301 		else
302 			strcpy(name, "(null)");
303 	}
304 	return fetchCpt(cptId);
305 }
306 
nameForType(uint16 type)307 const char *SkyCompact::nameForType(uint16 type) {
308 	if (type >= NUM_CPT_TYPES)
309 		return "unknown";
310 	else
311 		return _typeNames[type];
312 }
313 
getSub(Compact * cpt,uint16 mode)314 uint16 SkyCompact::getSub(Compact *cpt, uint16 mode) {
315 	switch (mode) {
316 	case 0:
317 		return cpt->baseSub;
318 	case 2:
319 		return cpt->baseSub_off;
320 	case 4:
321 		return cpt->actionSub;
322 	case 6:
323 		return cpt->actionSub_off;
324 	case 8:
325 		return cpt->getToSub;
326 	case 10:
327 		return cpt->getToSub_off;
328 	case 12:
329 		return cpt->extraSub;
330 	case 14:
331 		return cpt->extraSub_off;
332 	default:
333 		error("Invalid Mode (%d)", mode);
334 	}
335 }
336 
setSub(Compact * cpt,uint16 mode,uint16 value)337 void SkyCompact::setSub(Compact *cpt, uint16 mode, uint16 value) {
338 	switch (mode) {
339 	case 0:
340 		cpt->baseSub = value;
341 		return;
342 	case 2:
343 		cpt->baseSub_off = value;
344 		return;
345 	case 4:
346 		cpt->actionSub = value;
347 		return;
348 	case 6:
349 		cpt->actionSub_off = value;
350 		return;
351 	case 8:
352 		cpt->getToSub = value;
353 		return;
354 	case 10:
355 		cpt->getToSub_off = value;
356 		return;
357 	case 12:
358 		cpt->extraSub = value;
359 		return;
360 	case 14:
361 		cpt->extraSub_off = value;
362 		return;
363 	default:
364 		error("Invalid Mode (%d)", mode);
365 	}
366 }
367 
getGrafixPtr(Compact * cpt)368 uint16 *SkyCompact::getGrafixPtr(Compact *cpt) {
369 	uint16 *gfxBase = (uint16 *)fetchCpt(cpt->grafixProgId);
370 	if (gfxBase == NULL)
371 		return NULL;
372 
373 	return gfxBase + cpt->grafixProgPos;
374 }
375 
376 /**
377  * Returns the n'th mega set specified by \a megaSet from Compact \a cpt.
378  */
getMegaSet(Compact * cpt)379 MegaSet *SkyCompact::getMegaSet(Compact *cpt) {
380 	switch (cpt->megaSet) {
381 	case 0:
382 		return &cpt->megaSet0;
383 	case NEXT_MEGA_SET:
384 		return &cpt->megaSet1;
385 	case NEXT_MEGA_SET*2:
386 		return &cpt->megaSet2;
387 	case NEXT_MEGA_SET*3:
388 		return &cpt->megaSet3;
389 	default:
390 		error("Invalid MegaSet (%d)", cpt->megaSet);
391 	}
392 }
393 
394 /**
395  \brief Returns the turn table for direction \a dir
396 	from Compact \a cpt in \a megaSet.
397 
398  Functionally equivalent to:
399  \verbatim
400  clear eax
401  mov al,20
402  mul (cpt[esi]).c_dir
403  add ax,(cpt[esi]).c_mega_set
404  lea eax,(cpt[esi+eax]).c_turn_table_up
405  \endverbatim
406 */
getTurnTable(Compact * cpt,uint16 dir)407 uint16 *SkyCompact::getTurnTable(Compact *cpt, uint16 dir) {
408 	MegaSet *m = getMegaSet(cpt);
409 	TurnTable *turnTable = (TurnTable *)fetchCpt(m->turnTableId);
410 	switch (dir) {
411 	case 0:
412 		return turnTable->turnTableUp;
413 	case 1:
414 		return turnTable->turnTableDown;
415 	case 2:
416 		return turnTable->turnTableLeft;
417 	case 3:
418 		return turnTable->turnTableRight;
419 	case 4:
420 		return turnTable->turnTableTalk;
421 	default:
422 		error("No TurnTable (%d) in MegaSet (%d)", dir, cpt->megaSet);
423 	}
424 }
425 
getCompactElem(Compact * cpt,uint16 off)426 void *SkyCompact::getCompactElem(Compact *cpt, uint16 off) {
427 	if (off < COMPACT_SIZE)
428 		return((uint8 *)cpt + compactOffsets[off]);
429 	off -= COMPACT_SIZE;
430 
431 	if (off < MEGASET_SIZE)
432 		return((uint8 *)&(cpt->megaSet0) + megaSetOffsets[off]);
433 
434 	off -= MEGASET_SIZE;
435 	if (off < TURNTABLE_SIZE)
436 		return ((uint8 *)fetchCpt(cpt->megaSet0.turnTableId) + turnTableOffsets[off]);
437 
438 	off -= TURNTABLE_SIZE;
439 	if (off < MEGASET_SIZE)
440 		return((uint8 *)&(cpt->megaSet1) + megaSetOffsets[off]);
441 
442 	off -= MEGASET_SIZE;
443 	if (off < TURNTABLE_SIZE)
444 		return ((uint8 *)fetchCpt(cpt->megaSet1.turnTableId) + turnTableOffsets[off]);
445 
446 	off -= TURNTABLE_SIZE;
447 	if (off < MEGASET_SIZE)
448 		return((uint8 *)&(cpt->megaSet2) + megaSetOffsets[off]);
449 
450 	off -= MEGASET_SIZE;
451 	if (off < TURNTABLE_SIZE)
452 		return ((uint8 *)fetchCpt(cpt->megaSet2.turnTableId) + turnTableOffsets[off]);
453 
454 	off -= TURNTABLE_SIZE;
455 	if (off < MEGASET_SIZE)
456 		return((uint8 *)&(cpt->megaSet3) + megaSetOffsets[off]);
457 
458 	off -= MEGASET_SIZE;
459 	if (off < TURNTABLE_SIZE)
460 		return ((uint8 *)fetchCpt(cpt->megaSet3.turnTableId) + turnTableOffsets[off]);
461 	off -= TURNTABLE_SIZE;
462 
463 	error("Offset %X out of bounds of compact", (int)(off + COMPACT_SIZE + 4 * MEGASET_SIZE + 4 * TURNTABLE_SIZE));
464 }
465 
createResetData(uint16 gameVersion)466 uint8 *SkyCompact::createResetData(uint16 gameVersion) {
467 	_cptFile->seek(_resetDataPos);
468 	uint32 dataSize = _cptFile->readUint16LE() * sizeof(uint16);
469 	uint16 *resetBuf = (uint16 *)malloc(dataSize);
470 	_cptFile->read(resetBuf, dataSize);
471 	uint16 numDiffs = _cptFile->readUint16LE();
472 	for (uint16 cnt = 0; cnt < numDiffs; cnt++) {
473 		uint16 version = _cptFile->readUint16LE();
474 		uint16 diffFields = _cptFile->readUint16LE();
475 		if (version == gameVersion) {
476 			for (uint16 diffCnt = 0; diffCnt < diffFields; diffCnt++) {
477 				uint16 pos = _cptFile->readUint16LE();
478 				resetBuf[pos] = TO_LE_16(_cptFile->readUint16LE());
479 			}
480 			return (uint8 *)resetBuf;
481 		} else
482 			_cptFile->seek(diffFields * 2 * sizeof(uint16), SEEK_CUR);
483 	}
484 	free(resetBuf);
485 	error("Unable to find reset data for Beneath a Steel Sky Version 0.0%03d", gameVersion);
486 }
487 
488 // - debugging functions
489 
findCptId(void * cpt)490 uint16 SkyCompact::findCptId(void *cpt) {
491 	for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
492 		for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
493 			if (_compacts[listCnt][elemCnt] == cpt)
494 				return (listCnt << 12) | elemCnt;
495 	// not found
496 	debug(1, "Id for Compact %p wasn't found", cpt);
497 	return 0;
498 }
499 
findCptId(const char * cptName)500 uint16 SkyCompact::findCptId(const char *cptName) {
501 	for (uint16 listCnt = 0; listCnt < _numDataLists; listCnt++)
502 		for (uint16 elemCnt = 0; elemCnt < _dataListLen[listCnt]; elemCnt++)
503 			if (_cptNames[listCnt][elemCnt] != 0)
504 				if (scumm_stricmp(cptName, _cptNames[listCnt][elemCnt]) == 0)
505 					return (listCnt << 12) | elemCnt;
506 	// not found
507 	debug(1, "Id for Compact %s wasn't found", cptName);
508 	return 0;
509 }
510 
giveNumDataLists()511 uint16 SkyCompact::giveNumDataLists() {
512 	return _numDataLists;
513 }
514 
giveDataListLen(uint16 listNum)515 uint16 SkyCompact::giveDataListLen(uint16 listNum) {
516 	if (listNum >= _numDataLists) // list doesn't exist
517 		return 0;
518 	else
519 		return _dataListLen[listNum];
520 }
521 
522 const char *const SkyCompact::_typeNames[NUM_CPT_TYPES] = {
523 	"null",
524 	"COMPACT",
525 	"TURNTABLE",
526 	"ANIM SEQ",
527 	"UNKNOWN",
528 	"GETTOTABLE",
529 	"AR BUFFER",
530 	"MAIN LIST"
531 };
532 
533 } // End of namespace Sky
534