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.
22 #include "BAMImporter.h"
23 #include "Interface.h"
24 #include "Compressor.h"
25 #include "FileStream.h"
29 BAMImporter::BAMImporter(void)
40 BAMImporter::~BAMImporter(void)
42 if (str
&& autoFree
) {
47 gamedata
->FreePalette(palette
);
50 bool BAMImporter::Open(DataStream
* stream
, bool autoFree
)
57 if (str
&& this->autoFree
) {
62 gamedata
->FreePalette(palette
);
65 this->autoFree
= autoFree
;
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" );
75 //File was previously cached, using local copy
79 fclose( exist_in_cache
);
80 FileStream
* s
= new FileStream();
83 str
->Read( Signature
, 8 );
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" );
92 FILE* newfile
= fopen( cpath
, "wb" );
94 printMessage("BAMImporter", " ", RED
);
95 printf( "Cannot write %s.\n", cpath
);
98 Compressor
* comp
= ( Compressor
* )
99 core
->GetInterface( IE_COMPRESSION_CLASS_ID
);
100 comp
->Decompress( newfile
, str
);
105 FileStream
* s
= new FileStream();
108 str
->Read( Signature
, 8 );
111 if (strncmp( Signature
, "BAM V1 ", 8 ) != 0) {
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
++) {
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
;
152 int BAMImporter::GetCycleSize(unsigned char Cycle
)
154 if(Cycle
>= CyclesCount
) {
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
)
167 bool RLECompressed
= (frames
[findex
].FrameData
& 0x80000000) == 0;
170 const unsigned char* framedata
= data
;
171 framedata
+= (frames
[findex
].FrameData
& 0x7FFFFFFF) - DataStart
;
173 spr
= core
->GetVideoDriver()->CreateSpriteBAM8(
174 frames
[findex
].Width
, frames
[findex
].Height
,
175 true, framedata
, datasrc
, palette
, CompressedColorIndex
);
177 spr
= core
->GetVideoDriver()->CreateSpriteBAM8(
178 frames
[findex
].Width
, frames
[findex
].Height
, false,
179 framedata
, datasrc
, palette
, CompressedColorIndex
);
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();
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 );
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
) {
219 unsigned char* inpix
;
220 inpix
= (unsigned char*)malloc( RLESize
);
221 if (str
->Read( inpix
, RLESize
) == GEM_ERROR
) {
225 unsigned char * p
= inpix
;
226 unsigned char * Buffer
= (unsigned char*)pixels
;
228 while (i
< pixelcount
) {
229 if (*p
== CompressedColorIndex
) {
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
);
240 memset( &Buffer
[i
], CompressedColorIndex
, ( *p
) + 1 );
250 str
->Read( pixels
, pixelcount
);
255 ieWord
* BAMImporter::CacheFLT(unsigned int &count
)
260 for (i
= 0; i
< CyclesCount
; i
++) {
261 unsigned int tmp
= cycles
[i
].FirstFrame
+ cycles
[i
].FramesCount
;
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
) );
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
);
300 for (i
= 0; i
< CyclesCount
; ++i
) {
301 af
->AddCycle( cycles
[i
] );
303 af
->LoadFLT ( FLT
, count
);
308 /** This function will load the Animation as a Font */
309 Font
* BAMImporter::GetFont()
316 ieWord
*FLT
= CacheFLT(Count
);
318 // Numeric fonts have all frames in single cycle
319 if (CyclesCount
> 1) {
325 for (i
= 0; i
< Count
; i
++) {
327 if (CyclesCount
> 1) {
328 index
= FLT
[cycles
[i
].FirstFrame
];
329 if (index
>= FramesCount
)
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
++) {
343 if (CyclesCount
> 1) {
344 index
= FLT
[cycles
[i
].FirstFrame
];
345 if (index
>= FramesCount
) {
346 fnt
->AddChar( NULL
, 0, 0, 0, 0 );
353 unsigned char* pixels
= (unsigned char*)GetFramePixels( index
);
355 fnt
->AddChar( NULL
, 0, 0, 0, 0 );
358 fnt
->AddChar( pixels
, frames
[index
].Width
,
359 frames
[index
].Height
,
361 frames
[index
].YPos
);
366 fnt
->FinalizeSprite( true, 0 );
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
)