Plugin: Call Plugin::release directly, rather than through Interface and PluginMgr.
[gemrb.git] / gemrb / plugins / BAMImporter / BAMImporter.cpp
blobdbe0f7f0e6012eb122e4d9d46017086668897c77
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 #include "win32def.h"
22 #include "BAMImporter.h"
23 #include "Interface.h"
24 #include "Compressor.h"
25 #include "FileStream.h"
26 #include "Video.h"
27 #include "Palette.h"
29 BAMImporter::BAMImporter(void)
31 str = NULL;
32 autoFree = false;
33 frames = NULL;
34 cycles = NULL;
35 palette = NULL;
36 FramesCount = 0;
37 CyclesCount = 0;
40 BAMImporter::~BAMImporter(void)
42 if (str && autoFree) {
43 delete( str );
45 delete[] frames;
46 delete[] cycles;
47 gamedata->FreePalette(palette);
50 bool BAMImporter::Open(DataStream* stream, bool autoFree)
52 unsigned int i;
54 if (stream == NULL) {
55 return false;
57 if (str && this->autoFree) {
58 delete( str );
60 delete[] frames;
61 delete[] cycles;
62 gamedata->FreePalette(palette);
64 str = stream;
65 this->autoFree = autoFree;
66 char Signature[8];
67 str->Read( Signature, 8 );
68 if (strncmp( Signature, "BAMCV1 ", 8 ) == 0) {
69 //Check if Decompressed file has already been Cached
70 char cpath[_MAX_PATH];
71 strcpy( cpath, core->CachePath );
72 strcat( cpath, stream->filename );
73 FILE* exist_in_cache = fopen( cpath, "rb" );
74 if (exist_in_cache) {
75 //File was previously cached, using local copy
76 if (autoFree) {
77 delete( str );
79 fclose( exist_in_cache );
80 FileStream* s = new FileStream();
81 s->Open( cpath );
82 str = s;
83 str->Read( Signature, 8 );
84 } else {
85 //No file found in Cache, Decompressing and storing for further use
86 str->Seek( 4, GEM_CURRENT_POS );
88 if (!core->IsAvailable( IE_COMPRESSION_CLASS_ID )) {
89 printf( "No Compression Manager Available.\nCannot Load Compressed Bam File.\n" );
90 return false;
92 FILE* newfile = fopen( cpath, "wb" );
93 if (!newfile) {
94 printMessage("BAMImporter", " ", RED);
95 printf( "Cannot write %s.\n", cpath );
96 return false;
98 Compressor* comp = ( Compressor* )
99 core->GetInterface( IE_COMPRESSION_CLASS_ID );
100 comp->Decompress( newfile, str );
101 comp->release();
102 fclose( newfile );
103 if (autoFree)
104 delete( str );
105 FileStream* s = new FileStream();
106 s->Open( cpath );
107 str = s;
108 str->Read( Signature, 8 );
111 if (strncmp( Signature, "BAM V1 ", 8 ) != 0) {
112 return false;
114 str->ReadWord( &FramesCount );
115 str->Read( &CyclesCount, 1 );
116 str->Read( &CompressedColorIndex, 1 );
117 str->ReadDword( &FramesOffset );
118 str->ReadDword( &PaletteOffset );
119 str->ReadDword( &FLTOffset );
120 str->Seek( FramesOffset, GEM_STREAM_START );
121 frames = new FrameEntry[FramesCount];
122 DataStart = str->Size();
123 for (i = 0; i < FramesCount; i++) {
124 str->ReadWord( &frames[i].Width );
125 str->ReadWord( &frames[i].Height );
126 str->ReadWord( &frames[i].XPos );
127 str->ReadWord( &frames[i].YPos );
128 str->ReadDword( &frames[i].FrameData );
129 if ((frames[i].FrameData & 0x7FFFFFFF) < DataStart)
130 DataStart = (frames[i].FrameData & 0x7FFFFFFF);
132 cycles = new CycleEntry[CyclesCount];
133 for (i = 0; i < CyclesCount; i++) {
134 str->ReadWord( &cycles[i].FramesCount );
135 str->ReadWord( &cycles[i].FirstFrame );
137 str->Seek( PaletteOffset, GEM_STREAM_START );
138 palette = new Palette();
139 // no need to switch this
140 for (i = 0; i < 256; i++) {
141 RevColor rc;
142 str->Read( &rc, 4 );
143 palette->col[i].r = rc.r;
144 palette->col[i].g = rc.g;
145 palette->col[i].b = rc.b;
146 palette->col[i].a = rc.a;
149 return true;
152 int BAMImporter::GetCycleSize(unsigned char Cycle)
154 if(Cycle >= CyclesCount ) {
155 return -1;
157 return cycles[Cycle].FramesCount;
160 Sprite2D* BAMImporter::GetFrameInternal(unsigned short findex, unsigned char mode,
161 bool BAMsprite, const unsigned char* data,
162 AnimationFactory* datasrc)
164 Sprite2D* spr = 0;
166 if (BAMsprite) {
167 bool RLECompressed = (frames[findex].FrameData & 0x80000000) == 0;
169 assert(data);
170 const unsigned char* framedata = data;
171 framedata += (frames[findex].FrameData & 0x7FFFFFFF) - DataStart;
172 if (RLECompressed) {
173 spr = core->GetVideoDriver()->CreateSpriteBAM8(
174 frames[findex].Width, frames[findex].Height,
175 true, framedata, datasrc, palette, CompressedColorIndex);
176 } else {
177 spr = core->GetVideoDriver()->CreateSpriteBAM8(
178 frames[findex].Width, frames[findex].Height, false,
179 framedata, datasrc, palette, CompressedColorIndex );
181 } else {
182 void* pixels = GetFramePixels(findex);
183 spr = core->GetVideoDriver()->CreateSprite8(
184 frames[findex].Width, frames[findex].Height, 8,
185 pixels, palette->col, true, 0 );
188 spr->XPos = (ieWordSigned)frames[findex].XPos;
189 spr->YPos = (ieWordSigned)frames[findex].YPos;
190 if (mode == IE_SHADED) {
191 // CHECKME: is this ever used? Should we modify the sprite's palette
192 // without creating a local copy for this sprite?
193 Palette* pal = spr->GetPalette();
194 pal->CreateShadedAlphaChannel();
195 pal->Release();
197 return spr;
200 void* BAMImporter::GetFramePixels(unsigned short findex)
202 if (findex >= FramesCount) {
203 findex = cycles[0].FirstFrame;
205 str->Seek( ( frames[findex].FrameData & 0x7FFFFFFF ), GEM_STREAM_START );
206 unsigned long pixelcount = frames[findex].Height * frames[findex].Width;
207 void* pixels = malloc( pixelcount );
208 bool RLECompressed = ( ( frames[findex].FrameData & 0x80000000 ) == 0 );
209 if (RLECompressed) {
210 //if RLE Compressed
211 unsigned long RLESize;
212 RLESize = ( unsigned long )
213 ( frames[findex].Width * frames[findex].Height * 3 ) / 2 + 1;
214 //without partial reads, we should be careful
215 unsigned long remains = str->Remains();
216 if (RLESize > remains) {
217 RLESize = remains;
219 unsigned char* inpix;
220 inpix = (unsigned char*)malloc( RLESize );
221 if (str->Read( inpix, RLESize ) == GEM_ERROR) {
222 free( inpix );
223 return NULL;
225 unsigned char * p = inpix;
226 unsigned char * Buffer = (unsigned char*)pixels;
227 unsigned int i = 0;
228 while (i < pixelcount) {
229 if (*p == CompressedColorIndex) {
230 p++;
231 // FIXME: Czech HOW has apparently broken frame
232 // #141 in REALMS.BAM. Maybe we should put
233 // this condition to #ifdef BROKEN_xx ?
234 // Or maybe rather put correct REALMS.BAM
235 // into override/ dir?
236 if (i + ( *p ) + 1 > pixelcount) {
237 memset( &Buffer[i], CompressedColorIndex, pixelcount - i );
238 printf ("Broken frame %d\n", findex);
239 } else {
240 memset( &Buffer[i], CompressedColorIndex, ( *p ) + 1 );
242 i += *p;
243 } else
244 Buffer[i] = *p;
245 p++;
246 i++;
248 free( inpix );
249 } else {
250 str->Read( pixels, pixelcount );
252 return pixels;
255 ieWord * BAMImporter::CacheFLT(unsigned int &count)
257 int i;
259 count = 0;
260 for (i = 0; i < CyclesCount; i++) {
261 unsigned int tmp = cycles[i].FirstFrame + cycles[i].FramesCount;
262 if (tmp > count) {
263 count = tmp;
266 ieWord * FLT = ( ieWord * ) calloc( count, sizeof(ieWord) );
267 str->Seek( FLTOffset, GEM_STREAM_START );
268 str->Read( FLT, count * sizeof(ieWord) );
269 if( DataStream::IsEndianSwitch() ) {
270 //msvc likes it as char *
271 swab( (char*) FLT, (char*) FLT, count * sizeof(ieWord) );
273 return FLT;
276 AnimationFactory* BAMImporter::GetAnimationFactory(const char* ResRef, unsigned char mode)
278 unsigned int i, count;
279 AnimationFactory* af = new AnimationFactory( ResRef );
280 ieWord *FLT = CacheFLT( count );
282 bool videoBAMsupport = core->GetVideoDriver()->SupportsBAMSprites();
283 unsigned char* data = NULL;
285 if (videoBAMsupport) {
286 str->Seek( DataStart, GEM_STREAM_START );
287 unsigned long length = str->Remains();
288 if (length == 0) return af;
289 //data = new unsigned char[length];
290 data = (unsigned char *) malloc(length);
291 str->Read( data, length );
292 af->SetFrameData(data);
295 for (i = 0; i < FramesCount; ++i) {
296 Sprite2D* frame = GetFrameInternal(i, mode, videoBAMsupport, data, af);
297 assert(!videoBAMsupport || frame->BAM);
298 af->AddFrame(frame);
300 for (i = 0; i < CyclesCount; ++i) {
301 af->AddCycle( cycles[i] );
303 af->LoadFLT ( FLT, count );
304 free (FLT);
305 return af;
308 /** This function will load the Animation as a Font */
309 Font* BAMImporter::GetFont()
311 unsigned int i;
313 int w = 0, h = 0;
314 unsigned int Count;
316 ieWord *FLT = CacheFLT(Count);
318 // Numeric fonts have all frames in single cycle
319 if (CyclesCount > 1) {
320 Count = CyclesCount;
321 } else {
322 Count = FramesCount;
325 for (i = 0; i < Count; i++) {
326 unsigned int index;
327 if (CyclesCount > 1) {
328 index = FLT[cycles[i].FirstFrame];
329 if (index >= FramesCount)
330 continue;
331 } else {
332 index = i;
335 w = w + frames[index].Width;
336 if (frames[index].Height > h)
337 h = frames[index].Height;
340 Font* fnt = new Font( w, h, palette );
341 for (i = 0; i < Count; i++) {
342 unsigned int index;
343 if (CyclesCount > 1) {
344 index = FLT[cycles[i].FirstFrame];
345 if (index >= FramesCount) {
346 fnt->AddChar( NULL, 0, 0, 0, 0 );
347 continue;
349 } else {
350 index = i;
353 unsigned char* pixels = (unsigned char*)GetFramePixels( index );
354 if( !pixels) {
355 fnt->AddChar( NULL, 0, 0, 0, 0 );
356 continue;
358 fnt->AddChar( pixels, frames[index].Width,
359 frames[index].Height,
360 frames[index].XPos,
361 frames[index].YPos );
362 free( pixels );
364 free( FLT );
366 fnt->FinalizeSprite( true, 0 );
368 return fnt;
370 /** Debug Function: Returns the Global Animation Palette as a Sprite2D Object.
371 If the Global Animation Palette is NULL, returns NULL. */
372 Sprite2D* BAMImporter::GetPalette()
374 unsigned char * pixels = ( unsigned char * ) malloc( 256 );
375 unsigned char * p = pixels;
376 for (int i = 0; i < 256; i++) {
377 *p++ = ( unsigned char ) i;
379 return core->GetVideoDriver()->CreateSprite8( 16, 16, 8, pixels, palette->col, false );
382 #include "plugindef.h"
384 GEMRB_PLUGIN(0x3AD6427A, "BAM File Importer")
385 PLUGIN_CLASS(IE_BAM_CLASS_ID, BAMImporter)
386 END_PLUGIN()