1 #include "Debug.h"
2 #include "HImage.h"
3 #include "MemMan.h"
4 #include "VObject.h"
5 #include "VObject_Blitters.h"
6 #include "VSurface.h"
7
8 #include <string_theory/format>
9 #include <string_theory/string>
10
11 #include <algorithm>
12 #include <iterator>
13 #include <stdexcept>
14
15 // ******************************************************************************
16 //
17 // Video Object SGP Module
18 //
19 // Video Objects are used to contain any imagery which requires blitting. The data
20 // is contained within a Direct Draw surface. Palette information is in both
21 // a Direct Draw Palette and a 16BPP palette structure for 8->16 BPP Blits.
22 // Blitting is done via Direct Draw as well as custum blitters. Regions are
23 // used to define local coordinates within the surface
24 //
25 // Second Revision: Dec 10, 1996, Andrew Emmons
26 //
27 // *******************************************************************************
28
29
30 static SGPVObject* gpVObjectHead = 0;
31
32
SGPVObject(SGPImage const * const img)33 SGPVObject::SGPVObject(SGPImage const* const img) :
34 flags_(),
35 palette16_(),
36 current_shade_(),
37 ppZStripInfo(),
38 #ifdef SGP_VIDEO_DEBUGGING
39 name_(),
40 code_(),
41 #endif
42 next_(gpVObjectHead)
43 {
44 std::fill(std::begin(pShades), std::end(pShades), nullptr);
45
46 if (!(img->fFlags & IMAGE_TRLECOMPRESSED))
47 {
48 throw std::runtime_error("Image for video object creation must be TRLE compressed");
49 }
50
51 ETRLEData TempETRLEData;
52 GetETRLEImageData(img, &TempETRLEData);
53
54 subregion_count_ = TempETRLEData.usNumberOfObjects;
55 etrle_object_ = TempETRLEData.pETRLEObject;
56 pix_data_ = static_cast<UINT8*>(TempETRLEData.pPixData);
57 pix_data_size_ = TempETRLEData.uiSizePixData;
58 bit_depth_ = img->ubBitDepth;
59
60 if (img->ubBitDepth == 8)
61 {
62 // create palette
63 const SGPPaletteEntry* const src_pal = img->pPalette;
64 Assert(src_pal != NULL);
65
66 SGPPaletteEntry* const pal = palette_.Allocate(256);
67 memcpy(pal, src_pal, sizeof(*pal) * 256);
68
69 palette16_ = Create16BPPPalette(pal);
70 current_shade_ = palette16_;
71 }
72
73 gpVObjectHead = this;
74 #ifdef SGP_VIDEO_DEBUGGING
75 ++guiVObjectSize;
76 #endif
77 }
78
79
~SGPVObject()80 SGPVObject::~SGPVObject()
81 {
82 for (SGPVObject** anchor = &gpVObjectHead;; anchor = &(*anchor)->next_)
83 {
84 if (*anchor != this) continue;
85 *anchor = next_;
86 #ifdef SGP_VIDEO_DEBUGGING
87 --guiVObjectSize;
88 #endif
89 break;
90 }
91
92 DestroyPalettes();
93
94 if (pix_data_) delete[] pix_data_;
95 if (etrle_object_) delete[] etrle_object_;
96
97 if (ppZStripInfo != NULL)
98 {
99 for (UINT32 usLoop = 0; usLoop < SubregionCount(); usLoop++)
100 {
101 if (ppZStripInfo[usLoop] != NULL)
102 {
103 delete[] ppZStripInfo[usLoop]->pbZChange;
104 delete ppZStripInfo[usLoop];
105 }
106 }
107 delete[] ppZStripInfo;
108 }
109
110 #ifdef SGP_VIDEO_DEBUGGING
111 if (name_) delete[] name_;
112 if (code_) delete[] code_;
113 #endif
114 }
115
116
CurrentShade(size_t const idx)117 void SGPVObject::CurrentShade(size_t const idx)
118 {
119 if (idx >= lengthof(pShades) || !pShades[idx])
120 {
121 throw std::logic_error("Tried to set invalid video object shade");
122 }
123 current_shade_ = pShades[idx];
124 }
125
126
SubregionProperties(size_t const idx) const127 ETRLEObject const& SGPVObject::SubregionProperties(size_t const idx) const
128 {
129 if (idx >= SubregionCount())
130 {
131 throw std::logic_error("Tried to access invalid subregion in video object");
132 }
133 return etrle_object_[idx];
134 }
135
136
PixData(ETRLEObject const & e) const137 UINT8 const* SGPVObject::PixData(ETRLEObject const& e) const
138 {
139 return &pix_data_[e.uiDataOffset];
140 }
141
142
143 #define COMPRESS_TRANSPARENT 0x80
144 #define COMPRESS_RUN_MASK 0x7F
145
146
GetETRLEPixelValue(UINT16 const usETRLEIndex,UINT16 const usX,UINT16 const usY) const147 UINT8 SGPVObject::GetETRLEPixelValue(UINT16 const usETRLEIndex, UINT16 const usX, UINT16 const usY) const
148 {
149 ETRLEObject const& pETRLEObject = SubregionProperties(usETRLEIndex);
150
151 if (usX >= pETRLEObject.usWidth || usY >= pETRLEObject.usHeight)
152 {
153 throw std::logic_error("Tried to get pixel from invalid coordinate");
154 }
155
156 // Assuming everything's okay, go ahead and look...
157 UINT8 const* pCurrent = PixData(pETRLEObject);
158
159 // Skip past all uninteresting scanlines
160 for (UINT16 usLoopY = 0; usLoopY < usY; usLoopY++)
161 {
162 while (*pCurrent != 0)
163 {
164 if (*pCurrent & COMPRESS_TRANSPARENT)
165 {
166 pCurrent++;
167 }
168 else
169 {
170 pCurrent += *pCurrent & COMPRESS_RUN_MASK;
171 }
172 }
173 }
174
175 // Now look in this scanline for the appropriate byte
176 UINT16 usLoopX = 0;
177 do
178 {
179 UINT16 ubRunLength = *pCurrent & COMPRESS_RUN_MASK;
180
181 if (*pCurrent & COMPRESS_TRANSPARENT)
182 {
183 if (usLoopX + ubRunLength >= usX) return 0;
184 pCurrent++;
185 }
186 else
187 {
188 if (usLoopX + ubRunLength >= usX)
189 {
190 // skip to the correct byte; skip at least 1 to get past the byte defining the run
191 pCurrent += (usX - usLoopX) + 1;
192 return *pCurrent;
193 }
194 else
195 {
196 pCurrent += ubRunLength + 1;
197 }
198 }
199 usLoopX += ubRunLength;
200 }
201 while (usLoopX < usX);
202
203 throw std::logic_error("Inconsistent video object data");
204 }
205
206
207 /* Destroys the palette tables of a video object. All memory is deallocated, and
208 * the pointers set to NULL. Be careful not to try and blit this object until
209 * new tables are calculated, or things WILL go boom. */
DestroyPalettes()210 void SGPVObject::DestroyPalettes()
211 {
212 FOR_EACH(UINT16*, i, pShades)
213 {
214 if (flags_ & SHADETABLE_SHARED) continue;
215 UINT16* const p = *i;
216 if (!p) continue;
217 if (palette16_ == p) palette16_ = 0;
218 *i = 0;
219 delete[] p;
220 }
221
222 if (UINT16* const p = palette16_)
223 {
224 palette16_ = 0;
225 delete[] p;
226 }
227
228 current_shade_ = 0;
229 }
230
231
ShareShadetables(SGPVObject * const other)232 void SGPVObject::ShareShadetables(SGPVObject* const other)
233 {
234 flags_ |= SHADETABLE_SHARED;
235 for (size_t i = 0; i < lengthof(pShades); ++i)
236 {
237 pShades[i] = other->pShades[i];
238 }
239 }
240
241
InitializeVideoObjectManager(void)242 void InitializeVideoObjectManager(void)
243 {
244 //Shouldn't be calling this if the video object manager already exists.
245 //Call shutdown first...
246 Assert(gpVObjectHead == NULL);
247 gpVObjectHead = NULL;
248 }
249
250
ShutdownVideoObjectManager(void)251 void ShutdownVideoObjectManager(void)
252 {
253 while (gpVObjectHead)
254 {
255 delete gpVObjectHead;
256 }
257 }
258
259
260 #ifdef SGP_VIDEO_DEBUGGING
261 static
262 #endif
AddStandardVideoObjectFromHImage(SGPImage * const img)263 SGPVObject* AddStandardVideoObjectFromHImage(SGPImage* const img)
264 {
265 return new SGPVObject(img);
266 }
267
268
269 #ifdef SGP_VIDEO_DEBUGGING
270 static
271 #endif
AddStandardVideoObjectFromFile(const char * const ImageFile)272 SGPVObject* AddStandardVideoObjectFromFile(const char* const ImageFile)
273 {
274 AutoSGPImage hImage(CreateImage(ImageFile, IMAGE_ALLIMAGEDATA));
275 return AddStandardVideoObjectFromHImage(hImage.get());
276 }
277
278
BltVideoObject(SGPVSurface * const dst,SGPVObject const * const src,UINT16 const usRegionIndex,INT32 const iDestX,INT32 const iDestY)279 void BltVideoObject(SGPVSurface* const dst, SGPVObject const* const src, UINT16 const usRegionIndex, INT32 const iDestX, INT32 const iDestY)
280 {
281 Assert(src->BPP() == 8);
282 Assert(dst->BPP() == 16);
283
284 SGPVSurface::Lock l(dst);
285 UINT16* const pBuffer = l.Buffer<UINT16>();
286 UINT32 const uiPitch = l.Pitch();
287
288 if (BltIsClipped(src, iDestX, iDestY, usRegionIndex, &ClippingRect))
289 {
290 Blt8BPPDataTo16BPPBufferTransparentClip(pBuffer, uiPitch, src, iDestX, iDestY, usRegionIndex, &ClippingRect);
291 }
292 else
293 {
294 Blt8BPPDataTo16BPPBufferTransparent(pBuffer, uiPitch, src, iDestX, iDestY, usRegionIndex);
295 }
296 }
297
298
BltVideoObjectOutline(SGPVSurface * const dst,SGPVObject const * const hSrcVObject,UINT16 const usIndex,INT32 const iDestX,INT32 const iDestY,INT16 const s16BPPColor)299 void BltVideoObjectOutline(SGPVSurface* const dst, SGPVObject const* const hSrcVObject, UINT16 const usIndex, INT32 const iDestX, INT32 const iDestY, INT16 const s16BPPColor)
300 {
301 SGPVSurface::Lock l(dst);
302 UINT16* const pBuffer = l.Buffer<UINT16>();
303 UINT32 const uiPitch = l.Pitch();
304
305 if (BltIsClipped(hSrcVObject, iDestX, iDestY, usIndex, &ClippingRect))
306 {
307 Blt8BPPDataTo16BPPBufferOutlineClip(pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor, &ClippingRect);
308 }
309 else
310 {
311 Blt8BPPDataTo16BPPBufferOutline(pBuffer, uiPitch, hSrcVObject, iDestX, iDestY, usIndex, s16BPPColor);
312 }
313 }
314
315
BltVideoObjectOutlineShadow(SGPVSurface * const dst,const SGPVObject * const src,const UINT16 usIndex,const INT32 iDestX,const INT32 iDestY)316 void BltVideoObjectOutlineShadow(SGPVSurface* const dst, const SGPVObject* const src, const UINT16 usIndex, const INT32 iDestX, const INT32 iDestY)
317 {
318 SGPVSurface::Lock l(dst);
319 UINT16* const pBuffer = l.Buffer<UINT16>();
320 UINT32 const uiPitch = l.Pitch();
321
322 if (BltIsClipped(src, iDestX, iDestY, usIndex, &ClippingRect))
323 {
324 Blt8BPPDataTo16BPPBufferOutlineShadowClip(pBuffer, uiPitch, src, iDestX, iDestY, usIndex, &ClippingRect);
325 }
326 else
327 {
328 Blt8BPPDataTo16BPPBufferOutlineShadow(pBuffer, uiPitch, src, iDestX, iDestY, usIndex);
329 }
330 }
331
332
BltVideoObjectOnce(SGPVSurface * const dst,char const * const filename,UINT16 const region,INT32 const x,INT32 const y)333 void BltVideoObjectOnce(SGPVSurface* const dst, char const* const filename, UINT16 const region, INT32 const x, INT32 const y)
334 {
335 AutoSGPVObject vo(AddVideoObjectFromFile(filename));
336 BltVideoObject(dst, vo.get(), region, x, y);
337 }
338
339
340 #ifdef SGP_VIDEO_DEBUGGING
341
342 UINT32 guiVObjectSize = 0;
343
344
345 struct DUMPINFO
346 {
347 UINT32 Counter;
348 char Name[256];
349 char Code[256];
350 };
351
352
DumpVObjectInfoIntoFile(const char * filename,BOOLEAN fAppend)353 static void DumpVObjectInfoIntoFile(const char* filename, BOOLEAN fAppend)
354 {
355 if (guiVObjectSize == 0) return;
356
357 RustPointer<File> file(File_open(filename, fAppend ? FILE_OPEN_APPEND : FILE_OPEN_WRITE));
358 if (!file)
359 {
360 RustPointer<char> err(getRustError());
361 SLOGA("DumpVObjectInfoIntoFile: %s", err.get());
362 return;
363 }
364
365 //Allocate enough strings and counters for each node.
366 DUMPINFO* const Info = new DUMPINFO[guiVObjectSize]{};
367
368 //Loop through the list and record every unique filename and count them
369 UINT32 uiUniqueID = 0;
370 for (SGPVObject const* i = gpVObjectHead; i; i = i->next_)
371 {
372 char const* const Name = i->name_;
373 char const* const Code = i->code_;
374 BOOLEAN fFound = FALSE;
375 for (UINT32 i = 0; i < uiUniqueID; i++)
376 {
377 if (strcasecmp(Name, Info[i].Name) == 0 && strcasecmp(Code, Info[i].Code) == 0)
378 { //same string
379 fFound = TRUE;
380 Info[i].Counter++;
381 break;
382 }
383 }
384 if (!fFound)
385 {
386 strcpy(Info[uiUniqueID].Name, Name);
387 strcpy(Info[uiUniqueID].Code, Code);
388 Info[uiUniqueID].Counter++;
389 uiUniqueID++;
390 }
391 }
392
393 //Now dump the info.
394 ST::string buf;
395 buf += "-----------------------------------------------\n";
396 buf += ST::format(ST::substitute_invalid, "{} unique vObject names exist in {} VObjects\n", uiUniqueID, guiVObjectSize);
397 buf += "-----------------------------------------------\n\n";
398 for (UINT32 i = 0; i < uiUniqueID; i++)
399 {
400 buf += ST::format(ST::substitute_invalid, "{} occurrences of {}\n{}\n\n", Info[i].Counter, Info[i].Name, Info[i].Code);
401 }
402 buf += "\n-----------------------------------------------\n\n";
403
404 //Free all memory associated with this operation.
405 delete[] Info;
406 if (!File_writeAll(file.get(), reinterpret_cast<const uint8_t*>(buf.c_str()), buf.size()))
407 {
408 RustPointer<char> err(getRustError());
409 SLOGW("DumpVObjectInfoIntoFile: %s", err.get());
410 }
411 }
412
413
414 //Debug wrapper for adding vObjects
RecordVObject(SGPVObject * const vo,const char * Filename,UINT32 uiLineNum,const char * pSourceFile)415 static void RecordVObject(SGPVObject* const vo, const char* Filename, UINT32 uiLineNum, const char* pSourceFile)
416 {
417 //record the filename of the vObject (some are created via memory though)
418 vo->name_ = new char[strlen(Filename) + 1]{};
419 strcpy(vo->name_, Filename);
420
421 //record the code location of the calling creating function.
422 char str[256];
423 sprintf(str, "%s -- line(%d)", pSourceFile, uiLineNum);
424 vo->code_ = new char[strlen(str) + 1]{};
425 strcpy(vo->code_, str);
426 }
427
428
AddAndRecordVObjectFromHImage(SGPImage * const img,UINT32 uiLineNum,const char * pSourceFile)429 SGPVObject* AddAndRecordVObjectFromHImage(SGPImage* const img, UINT32 uiLineNum, const char* pSourceFile)
430 {
431 SGPVObject* const vo = AddStandardVideoObjectFromHImage(img);
432 RecordVObject(vo, "<IMAGE>", uiLineNum, pSourceFile);
433 return vo;
434 }
435
436
AddAndRecordVObjectFromFile(const char * ImageFile,UINT32 uiLineNum,const char * pSourceFile)437 SGPVObject* AddAndRecordVObjectFromFile(const char* ImageFile, UINT32 uiLineNum, const char* pSourceFile)
438 {
439 SGPVObject* const vo = AddStandardVideoObjectFromFile(ImageFile);
440 RecordVObject(vo, ImageFile, uiLineNum, pSourceFile);
441 return vo;
442 }
443
444
PerformVideoInfoDumpIntoFile(const char * filename,BOOLEAN fAppend)445 void PerformVideoInfoDumpIntoFile(const char* filename, BOOLEAN fAppend)
446 {
447 DumpVObjectInfoIntoFile(filename, fAppend);
448 DumpVSurfaceInfoIntoFile(filename, TRUE);
449 }
450
451 #endif
452