diff --git a/CMakeLists.txt b/CMakeLists.txt index 8f0d036..09c5eb8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,8 @@ set(SOURCE_FILES SpaceCadetPinball/fullscrn.h SpaceCadetPinball/gdrv.cpp SpaceCadetPinball/gdrv.h + SpaceCadetPinball/GroupData.cpp + SpaceCadetPinball/GroupData.h SpaceCadetPinball/high_score.cpp SpaceCadetPinball/high_score.h SpaceCadetPinball/loader.cpp diff --git a/SpaceCadetPinball/GroupData.cpp b/SpaceCadetPinball/GroupData.cpp new file mode 100644 index 0000000..396e5b8 --- /dev/null +++ b/SpaceCadetPinball/GroupData.cpp @@ -0,0 +1,268 @@ +#include "pch.h" + +#include "GroupData.h" + +#include "fullscrn.h" +#include "gdrv.h" +#include "memory.h" +#include "zdrv.h" + + +EntryData::~EntryData() +{ + if (Buffer) + { + if (EntryType == FieldTypes::Bitmap8bit) + gdrv::destroy_bitmap(reinterpret_cast(Buffer)); + else if (EntryType == FieldTypes::Bitmap16bit) + zdrv::destroy_zmap(reinterpret_cast(Buffer)); + memory::free(Buffer); + } + if (DerivedBmp) + { + gdrv::destroy_bitmap(DerivedBmp); + memory::free(DerivedBmp); + } + if (DerivedZMap) + { + zdrv::destroy_zmap(DerivedZMap); + memory::free(DerivedZMap); + } +} + + +GroupData::GroupData(int groupId) +{ + GroupId = groupId; +} + +void GroupData::AddEntry(EntryData* entry) +{ + Entries.push_back(entry); + + switch (entry->EntryType) + { + case FieldTypes::GroupName: + GroupName = entry->Buffer; + break; + case FieldTypes::Bitmap8bit: + { + auto bmp = reinterpret_cast(entry->Buffer); + if (bmp->BitmapType == BitmapTypes::Spliced) + { + // Get rid of spliced bitmap early on, to simplify render pipeline + auto splitBmp = memory::allocate(); + auto splitZMap = memory::allocate(); + SplitSplicedBitmap(*bmp, *splitBmp, *splitZMap); + entry->DerivedBmp = splitBmp; + entry->DerivedZMap = splitZMap; + SetBitmap(splitBmp); + SetZMap(splitZMap); + } + else + { + SetBitmap(bmp); + } + break; + } + case FieldTypes::Bitmap16bit: + { + SetZMap(reinterpret_cast(entry->Buffer)); + break; + } + default: break; + } +} + +gdrv_bitmap8* GroupData::GetBitmap(int resolution) const +{ + return Bitmaps[resolution]; +} + +zmap_header_type* GroupData::GetZMap(int resolution) const +{ + return ZMaps[resolution]; +} + +void GroupData::SplitSplicedBitmap(const gdrv_bitmap8& srcBmp, gdrv_bitmap8& bmp, zmap_header_type& zMap) +{ + assertm(srcBmp.BitmapType == BitmapTypes::Spliced, "GroupData: wrong bitmap type"); + + gdrv::create_bitmap(&bmp, srcBmp.Width, srcBmp.Height, srcBmp.Width); + std::memset(bmp.IndexedBmpPtr, 0xff, bmp.Stride * bmp.Height); + bmp.XPosition = srcBmp.XPosition; + bmp.YPosition = srcBmp.YPosition; + bmp.Resolution = srcBmp.Resolution; + + zdrv::create_zmap(&zMap, srcBmp.Width, srcBmp.Height, srcBmp.Width); + zdrv::fill(&zMap, zMap.Width, zMap.Height, 0, 0, 0xFFFF); + zMap.Resolution = srcBmp.Resolution; + + auto tableWidth = fullscrn::resolution_array[srcBmp.Resolution].TableWidth; + auto src = reinterpret_cast(srcBmp.IndexedBmpPtr); + auto srcChar = reinterpret_cast(&src); + for (int dstInd = 0;;) + { + auto stride = static_cast(*src++); + if (stride < 0) + break; + + // Stride is in terms of dst stride, hardcoded to match vScreen width in current resolution + if (stride > bmp.Width) + { + stride += bmp.Width - tableWidth; + assertm(stride >= 0, "Spliced bitmap: negative computed stride"); + } + + dstInd += stride; + for (auto count = *src++; count; count--) + { + auto depth = *src++; + bmp.IndexedBmpPtr[dstInd] = **srcChar; + zMap.ZPtr1[dstInd] = depth; + + (*srcChar)++; + dstInd++; + } + } +} + +void GroupData::SetBitmap(gdrv_bitmap8* bmp) +{ + assertm(Bitmaps[bmp->Resolution] == nullptr, "GroupData: bitmap override"); + Bitmaps[bmp->Resolution] = bmp; + + auto zMap = ZMaps[bmp->Resolution]; + if (zMap) + { + assertm(bmp->Width == zMap->Width && bmp->Height == zMap->Height, + "GroupData: mismatched bitmap/zMap dimensions"); + } +} + +void GroupData::SetZMap(zmap_header_type* zMap) +{ + // Flip zMap to match with flipped non-indexed bitmaps + zdrv::FlipZMapHorizontally(*zMap); + + assertm(ZMaps[zMap->Resolution] == nullptr, "GroupData: zMap override"); + ZMaps[zMap->Resolution] = zMap; + + auto bmp = Bitmaps[zMap->Resolution]; + if (bmp) + { + assertm(bmp->Width == zMap->Width && bmp->Height == zMap->Height, + "GroupData: mismatched bitmap/zMap dimensions"); + } +} + + +DatFile::~DatFile() +{ + for (auto group : Groups) + { + if (!group) + continue; + + for (const auto entry : group->GetEntries()) + { + delete entry; + } + delete group; + } +} + +char* DatFile::field(int groupIndex, FieldTypes targetEntryType) +{ + assertm(targetEntryType != FieldTypes::Bitmap8bit && targetEntryType != FieldTypes::Bitmap16bit, + "partman: Use specific get for bitmaps"); + + auto group = Groups[groupIndex]; + for (const auto entry : group->GetEntries()) + { + if (entry->EntryType == targetEntryType) + { + return entry->Buffer; + } + if (entry->EntryType > targetEntryType) + break; + } + return nullptr; +} + + +char* DatFile::field_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN) +{ + assertm(targetEntryType != FieldTypes::Bitmap8bit && targetEntryType != FieldTypes::Bitmap16bit, + "partman: Use specific get for bitmaps"); + + auto group = Groups[groupIndex]; + auto skipCount = 0; + for (const auto entry : group->GetEntries()) + { + if (entry->EntryType > targetEntryType) + break; + if (entry->EntryType == targetEntryType) + if (skipCount++ == skipFirstN) + return entry->Buffer; + } + return nullptr; +} + +int DatFile::field_size_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN) +{ + auto group = Groups[groupIndex]; + auto skipCount = 0; + for (const auto entry : group->GetEntries()) + { + if (entry->EntryType > targetEntryType) + return 0; + if (entry->EntryType == targetEntryType) + if (skipCount++ == skipFirstN) + return entry->FieldSize; + } + return 0; +} + +int DatFile::field_size(int groupIndex, FieldTypes targetEntryType) +{ + return field_size_nth(groupIndex, targetEntryType, 0); +} + +int DatFile::record_labeled(LPCSTR targetGroupName) +{ + auto targetLength = strlen(targetGroupName); + for (int groupIndex = Groups.size() - 1; groupIndex >= 0; --groupIndex) + { + auto groupName = field(groupIndex, FieldTypes::GroupName); + if (!groupName) + continue; + + int index; + for (index = 0; index < targetLength; index++) + if (targetGroupName[index] != groupName[index]) + break; + if (index == targetLength && !targetGroupName[index] && !groupName[index]) + return groupIndex; + } + + return -1; +} + +char* DatFile::field_labeled(LPCSTR lpString, FieldTypes fieldType) +{ + auto groupIndex = record_labeled(lpString); + return groupIndex < 0 ? nullptr : field(groupIndex, fieldType); +} + +gdrv_bitmap8* DatFile::GetBitmap(int groupIndex) +{ + auto group = Groups[groupIndex]; + return group->GetBitmap(fullscrn::GetResolution()); +} + +zmap_header_type* DatFile::GetZMap(int groupIndex) +{ + auto group = Groups[groupIndex]; + return group->GetZMap(fullscrn::GetResolution()); +} diff --git a/SpaceCadetPinball/GroupData.h b/SpaceCadetPinball/GroupData.h new file mode 100644 index 0000000..d966fe6 --- /dev/null +++ b/SpaceCadetPinball/GroupData.h @@ -0,0 +1,97 @@ +#pragma once + +struct zmap_header_type; +struct gdrv_bitmap8; + + +enum class FieldTypes : int16_t +{ + // One 16 bit signed integer + ShortValue = 0, + + // Sprite bitmap, 8bpp, indexed color + Bitmap8bit = 1, + + Unknown2 = 2, + + // Group name, char[]. Not all groups have names. + GroupName = 3, + + Unknown4 = 4, + + // Palette, contains 256 RBGA 4-byte colors. + Palette = 5, + + Unknown6 = 6, + + Unknown7 = 7, + + Unknown8 = 8, + + // String, char[] + String = 9, + + // Array of 16 bit signed integers + ShortArray = 10, + + // Array of 32 bit floats + FloatArray = 11, + + // Sprite depth map, 16bpp, unsigned + Bitmap16bit = 12, +}; + +struct EntryData +{ + ~EntryData(); + FieldTypes EntryType{}; + int FieldSize{}; + char* Buffer{}; + gdrv_bitmap8* DerivedBmp{}; + zmap_header_type* DerivedZMap{}; +}; + +class GroupData +{ +public: + int GroupId; + std::string GroupName; + + GroupData(int groupId); + void AddEntry(EntryData* entry); + const std::vector& GetEntries() const { return Entries; } + const EntryData* GetEntry(size_t index) const { return Entries[index]; } + size_t EntryCount() const { return Entries.size(); } + void ReserveEntries(size_t count) { Entries.reserve(count); } + gdrv_bitmap8* GetBitmap(int resolution) const; + zmap_header_type* GetZMap(int resolution) const; + +private: + std::vector Entries; + gdrv_bitmap8* Bitmaps[3]{}; + zmap_header_type* ZMaps[3]{}; + + static void SplitSplicedBitmap(const gdrv_bitmap8& srcBmp, gdrv_bitmap8& bmp, zmap_header_type& zMap); + + void SetBitmap(gdrv_bitmap8* bmp); + void SetZMap(zmap_header_type* zMap); +}; + + +class DatFile +{ +public: + std::string AppName; + std::string Description; + std::vector Groups; + + ~DatFile(); + char* field_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN); + char* field(int groupIndex, FieldTypes entryType); + int field_size_nth(int groupIndex, FieldTypes targetEntryType, int skipFirstN); + int field_size(int groupIndex, FieldTypes targetEntryType); + int record_labeled(LPCSTR targetGroupName); + char* field_labeled(LPCSTR lpString, FieldTypes fieldType); + gdrv_bitmap8* GetBitmap(int groupIndex); + zmap_header_type* GetZMap(int groupIndex); +}; diff --git a/SpaceCadetPinball/SpaceCadetPinball.cpp b/SpaceCadetPinball/SpaceCadetPinball.cpp index fe7897f..a7c0263 100644 --- a/SpaceCadetPinball/SpaceCadetPinball.cpp +++ b/SpaceCadetPinball/SpaceCadetPinball.cpp @@ -3,15 +3,6 @@ #include "pch.h" -#include "objlist_class.h" -#include "partman.h" -#include "gdrv.h" -#include "loader.h" -#include "pb.h" -#include "pinball.h" -#include "score.h" -#include "TPinballTable.h" -#include "TTextBox.h" #include "winmain.h" int main(int argc, char* argv[]) @@ -24,57 +15,6 @@ int main(int argc, char* argv[]) winmain::WinMain(cmdLine.c_str()); return 0; } - - std::cout << "Hello World!\n"; - gdrv::init(0,0); - - auto d = objlist_class(2, 4); - for (size_t i = 0; i < 100; i++) - { - d.Add((void*)i); - } - d.Delete((void*)3); - - auto xx = sizeof(datFileHeader); - - winmain::DatFileName = "PINBALL.DAT"; - pb::init(); - auto datFile = pb::record_table; - - assert(partman::field_size_nth(datFile, 0, datFieldTypes::String, 0) == 43); - assert(partman::field_size_nth(datFile, 2, datFieldTypes::Palette, 0) == 1024); - assert(partman::field_size_nth(datFile, 101, datFieldTypes::FloatArray, 4) == 32); - - assert(strcmp(partman::field(datFile, 0, datFieldTypes::String), "3D-Pinball: Copyright 1994, Cinematronics") == 0); - assert(strcmp(partman::field(datFile, 540, datFieldTypes::GroupName), "table_objects") == 0); - - assert(partman::record_labeled(datFile, "background") == 2); - assert(partman::record_labeled(datFile, "a_bump1") == 372); - - assert(memcmp(partman::field_labeled(datFile, "table_size", datFieldTypes::ShortArray), new short[2]{ 600, 416 }, 2 * 2) == 0); - - //loader::error(25, 26); - loader::get_sound_id(18); - visualStruct visual1{}; - loader::material(96, &visual1); - loader::query_visual(283, 0, &visual1); - visualKickerStruct kicker1{}; - loader::kicker(509, &kicker1); - - auto score1 = score::create("score1", nullptr); - - auto pinballTable = pb::MainTable; - //pinballTable->find_component(1); - - for (int i = 0; i < 190; i++) - { - auto rsc = pinball::get_rc_string(i, 0); - if (rsc) - printf("%d:\t%s\n", i, rsc); - } - - //DatParser::Parse(dataFileName); - std::cout << "Goodby World!\n"; } // Run program: Ctrl + F5 or Debug > Start Without Debugging menu diff --git a/SpaceCadetPinball/TFlipper.cpp b/SpaceCadetPinball/TFlipper.cpp index 5af7c19..0092c97 100644 --- a/SpaceCadetPinball/TFlipper.cpp +++ b/SpaceCadetPinball/TFlipper.cpp @@ -9,6 +9,7 @@ #include "render.h" #include "TFlipperEdge.h" #include "timer.h" +#include "TPinballTable.h" TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(table, groupIndex, false) { diff --git a/SpaceCadetPinball/control.cpp b/SpaceCadetPinball/control.cpp index f402eaf..011ec09 100644 --- a/SpaceCadetPinball/control.cpp +++ b/SpaceCadetPinball/control.cpp @@ -3,6 +3,7 @@ #include "objlist_class.h" #include "pb.h" +#include "pinball.h" #include "TBlocker.h" #include "TBumper.h" #include "TComponentGroup.h" @@ -27,6 +28,7 @@ #include "TRamp.h" #include "TPlunger.h" #include "TWall.h" +#include "TTextBox.h" int control::pbctrl_state; diff --git a/SpaceCadetPinball/gdrv.cpp b/SpaceCadetPinball/gdrv.cpp index 6b67f71..fe7fa67 100644 --- a/SpaceCadetPinball/gdrv.cpp +++ b/SpaceCadetPinball/gdrv.cpp @@ -1,11 +1,15 @@ #include "pch.h" #include "gdrv.h" +#include "GroupData.h" #include "memory.h" +#include "partman.h" +#include "pb.h" +#include "score.h" #include "winmain.h" SDL_Texture* gdrv::vScreenTex = nullptr; -char* gdrv::vScreenPixels = nullptr; +ColorRgba* gdrv::vScreenPixels = nullptr; int gdrv::vScreenWidth, gdrv::vScreenHeight; ColorRgba gdrv::current_palette[256]{}; SDL_Rect gdrv::DestinationRect{}; @@ -20,7 +24,7 @@ int gdrv::init(int width, int height) SDL_TEXTUREACCESS_STREAMING, width, height ); - vScreenPixels = memory::allocate(width * height * 4); + vScreenPixels = memory::allocate(width * height); vScreenWidth = width; vScreenHeight = height; @@ -38,53 +42,84 @@ void gdrv::get_focus() { } -int gdrv::create_bitmap(gdrv_bitmap8* bmp, int width, int height) +int gdrv::create_bitmap(gdrv_bitmap8* bmp, int width, int height, int stride, bool indexed) { - bmp->Width = width; - bmp->Stride = width; - if (width % 4) - bmp->Stride = 4 - width % 4 + width; + assertm(width >= 0 && height >= 0, "Negative bitmap8 dimensions"); + bmp->Width = width; bmp->Height = height; + bmp->Stride = width; bmp->BitmapType = BitmapTypes::DibBitmap; - bmp->BmpBufPtr1 = memory::allocate(bmp->Height * bmp->Stride); - return 0; + bmp->Texture = nullptr; + + if (stride >= 0) + bmp->IndexedStride = stride; + else + { + bmp->IndexedStride = width; + if (width % 4) + bmp->IndexedStride = width - width % 4 + 4; + } + + if (indexed) + bmp->IndexedBmpPtr = memory::allocate(bmp->Height * bmp->IndexedStride); + bmp->BmpBufPtr1 = memory::allocate(bmp->Height * bmp->Stride); + if (bmp->BmpBufPtr1) + { + return 0; + } + return -1; } -int gdrv::create_raw_bitmap(gdrv_bitmap8* bmp, int width, int height, int flag) +int gdrv::create_bitmap(gdrv_bitmap8& bmp, const dat8BitBmpHeader& header) { - bmp->Width = width; - bmp->Stride = width; - if (flag && width % 4) - bmp->Stride = width - width % 4 + 4; - unsigned int sizeInBytes = height * bmp->Stride; - bmp->Height = height; - bmp->BitmapType = BitmapTypes::RawBitmap; - char* buf = memory::allocate(sizeInBytes); - bmp->BmpBufPtr1 = buf; - if (!buf) - return -1; - return 0; -} + assertm(header.Width >= 0 && header.Height >= 0, "Negative bitmap8 dimensions"); -int gdrv::create_spliced_bitmap(gdrv_bitmap8* bmp, int width, int height, int size) -{ - bmp->Width = width; - bmp->Stride = width; - bmp->BitmapType = BitmapTypes::Spliced; - bmp->Height = height; - char* buf = memory::allocate(size); - bmp->BmpBufPtr1 = buf; - if (!buf) - return -1; - return 0; + if (header.IsFlagSet(bmp8Flags::Spliced)) + bmp.BitmapType = BitmapTypes::Spliced; + else if (header.IsFlagSet(bmp8Flags::DibBitmap)) + bmp.BitmapType = BitmapTypes::DibBitmap; + else + bmp.BitmapType = BitmapTypes::RawBitmap; + + bmp.Width = header.Width; + bmp.Stride = header.Width; + bmp.IndexedStride = header.Width; + bmp.Height = header.Height; + bmp.XPosition = header.XPosition; + bmp.YPosition = header.YPosition; + bmp.Resolution = header.Resolution; + bmp.Texture = nullptr; + + int sizeInBytes; + if (bmp.BitmapType == BitmapTypes::Spliced) + { + sizeInBytes = header.Size; + } + else + { + if (bmp.BitmapType == BitmapTypes::RawBitmap) + assertm(bmp.Width % 4 == 0 || header.IsFlagSet(bmp8Flags::RawBmpUnaligned), "Wrong raw bitmap align flag"); + if (bmp.Width % 4) + bmp.IndexedStride = bmp.Width - bmp.Width % 4 + 4; + sizeInBytes = bmp.Height * bmp.IndexedStride; + assertm(sizeInBytes == header.Size, "Wrong bitmap8 size"); + } + + bmp.IndexedBmpPtr = memory::allocate(sizeInBytes); + bmp.BmpBufPtr1 = memory::allocate(bmp.Stride * bmp.Height); + if (bmp.BmpBufPtr1) + { + return 0; + } + return -1; } int gdrv::display_palette(ColorRgba* plt) { const uint32_t sysPaletteColors[] { - 0x00000000, + 0x00000000, // Color 0: transparent 0x00000080, 0x00008000, 0x00008080, @@ -122,6 +157,19 @@ int gdrv::display_palette(ColorRgba* plt) current_palette[255].rgba.peGreen = -1; current_palette[255].rgba.peRed = -1; + score::ApplyPalette(); + for (const auto group : pb::record_table->Groups) + { + for (int i = 0; i <= 2; i++) + { + auto bmp = group->GetBitmap(i); + if (bmp) + { + ApplyPalette(*bmp); + } + } + } + return 0; } @@ -134,6 +182,10 @@ int gdrv::destroy_bitmap(gdrv_bitmap8* bmp) if (bmp->BitmapType != BitmapTypes::None) { memory::free(bmp->BmpBufPtr1); + if (bmp->IndexedBmpPtr) + memory::free(bmp->IndexedBmpPtr); + if (bmp->Texture) + SDL_DestroyTexture(bmp->Texture); } memset(bmp, 0, sizeof(gdrv_bitmap8)); return 0; @@ -151,7 +203,7 @@ void gdrv::blit(gdrv_bitmap8* bmp, int xSrc, int ySrc, int xDest, int yDest, int { StretchDIBitsScaled( xSrc, - ySrc , + ySrc, xDest, yDest, width, @@ -173,51 +225,43 @@ void gdrv::blat(gdrv_bitmap8* bmp, int xDest, int yDest) ); } -void gdrv::fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, char fillChar) +void gdrv::fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, uint8_t fillChar) { - int bmpHeight = bmp->Height; - if (bmpHeight < 0) - bmpHeight = -bmpHeight; - char* bmpPtr = &bmp->BmpBufPtr1[bmp->Width * (bmpHeight - height - yOff) + xOff]; + auto color = current_palette[fillChar]; + auto bmpPtr = &bmp->BmpBufPtr1[bmp->Width * yOff + xOff]; for (; height > 0; --height) { - if (width > 0) - memset(bmpPtr, fillChar, width); - bmpPtr += bmp->Stride; + for (int x = width; x > 0; --x) + *bmpPtr++ = color; + bmpPtr += bmp->Stride - width; } } void gdrv::copy_bitmap(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff) { - int dstHeight = abs(dstBmp->Height); - int srcHeight = abs(srcBmp->Height); - char* srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * (srcHeight - height - srcYOff) + srcXOff]; - char* dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * (dstHeight - height - yOff) + xOff]; + auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * srcYOff + srcXOff]; + auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * yOff + xOff]; for (int y = height; y > 0; --y) { - for (int x = width; x > 0; --x) - *dstPtr++ = *srcPtr++; - - srcPtr += srcBmp->Stride - width; - dstPtr += dstBmp->Stride - width; + std::memcpy(dstPtr, srcPtr, width * sizeof(ColorRgba)); + srcPtr += srcBmp->Stride; + dstPtr += dstBmp->Stride; } } void gdrv::copy_bitmap_w_transparency(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff) { - int dstHeight = abs(dstBmp->Height); - int srcHeight = abs(srcBmp->Height); - char* srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * (srcHeight - height - srcYOff) + srcXOff]; - char* dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * (dstHeight - height - yOff) + xOff]; + auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * srcYOff + srcXOff]; + auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * yOff + xOff]; for (int y = height; y > 0; --y) { for (int x = width; x > 0; --x) { - if (*srcPtr) + if ((*srcPtr).Color) *dstPtr = *srcPtr; ++srcPtr; ++dstPtr; @@ -230,19 +274,15 @@ void gdrv::copy_bitmap_w_transparency(gdrv_bitmap8* dstBmp, int width, int heigh void gdrv::grtext_draw_ttext_in_box(LPCSTR text, int xOff, int yOff, int width, int height, int a6) -{ +{ } int gdrv::StretchDIBitsScaled(int xSrc, int ySrc, int xDst, int yDst, int width, int height, gdrv_bitmap8* bmp) { - // Y is inverted, X normal left to right - ySrc = std::max(0, std::min(bmp->Height - height, bmp->Height)) - ySrc; - yDst = std::max(0, std::min(vScreenHeight - height, vScreenHeight)) - yDst; - // Negative dst == positive src offset if (xDst < 0) - { + { xSrc -= xDst; xDst = 0; } @@ -258,27 +298,22 @@ int gdrv::StretchDIBitsScaled(int xSrc, int ySrc, int xDst, int yDst, if (xSrc + width > bmp->Width) width = bmp->Width - xSrc; if (ySrc + height > bmp->Height) - height = bmp->Height - ySrc; + height = bmp->Height - ySrc; xDst = std::max(0, std::min(xDst, vScreenWidth)); yDst = std::max(0, std::min(yDst, vScreenHeight)); if (xDst + width > vScreenWidth) width = vScreenWidth - xDst; if (yDst + height > vScreenHeight) - height = vScreenHeight - yDst; + height = vScreenHeight - yDst; - auto srcPtr = reinterpret_cast(&bmp->BmpBufPtr1[bmp->Stride * ySrc + xSrc]); - auto dstPtr = &reinterpret_cast(vScreenPixels)[vScreenWidth * yDst + xDst]; + auto srcPtr = &bmp->BmpBufPtr1[bmp->Stride * ySrc + xSrc]; + auto dstPtr = &vScreenPixels[vScreenWidth * yDst + xDst]; for (int y = height; y > 0; --y) { - for (int x = width; x > 0; --x) - { - - *dstPtr++ = current_palette[*srcPtr++].Color; - } - - srcPtr += bmp->Stride - width; - dstPtr += vScreenWidth - width; + std::memcpy(dstPtr, srcPtr, width * sizeof(ColorRgba)); + srcPtr += bmp->Stride; + dstPtr += vScreenWidth; } return 0; @@ -297,5 +332,41 @@ void gdrv::BlitScreen() ); std::memcpy(lockedPixels, vScreenPixels, vScreenWidth * vScreenHeight * 4); SDL_UnlockTexture(vScreenTex); - SDL_RenderCopyEx(winmain::Renderer, vScreenTex, nullptr, &DestinationRect, 0, nullptr, SDL_FLIP_VERTICAL); + SDL_RenderCopyEx(winmain::Renderer, vScreenTex, nullptr, &DestinationRect, 0, nullptr, SDL_FLIP_NONE); +} + +void gdrv::ApplyPalette(gdrv_bitmap8& bmp) +{ + if (bmp.BitmapType == BitmapTypes::None) + return; + assertm(bmp.BitmapType != BitmapTypes::Spliced, "gdrv: wrong bitmap type"); + assertm(bmp.IndexedBmpPtr != nullptr, "gdrv: non-indexed bitmap"); + + // Apply palette, flip horizontally + auto dst = bmp.BmpBufPtr1; + for (auto y = bmp.Height - 1; y >= 0; y--) + { + auto src = reinterpret_cast(bmp.IndexedBmpPtr) + bmp.IndexedStride * y; + for (auto x = 0; x < bmp.Width; x++) + { + *dst++ = current_palette[*src++]; + } + } +} + +void gdrv::CreatePreview(gdrv_bitmap8& bmp) +{ + if (bmp.Texture) + return; + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + auto texture = SDL_CreateTexture + ( + winmain::Renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + bmp.Width, bmp.Height + ); + SDL_UpdateTexture(texture, nullptr, bmp.BmpBufPtr1, bmp.Width * 4); + bmp.Texture = texture; } diff --git a/SpaceCadetPinball/gdrv.h b/SpaceCadetPinball/gdrv.h index 851eb0d..2f93585 100644 --- a/SpaceCadetPinball/gdrv.h +++ b/SpaceCadetPinball/gdrv.h @@ -1,23 +1,13 @@ #pragma once -enum class BitmapTypes : char +enum class BitmapTypes : uint8_t { None = 0, RawBitmap = 1, DibBitmap = 2, - Spliced = 4, + Spliced = 3, }; -struct gdrv_bitmap8 -{ - char* BmpBufPtr1; - int Width; - int Height; - int Stride; - BitmapTypes BitmapType; - int XPosition; - int YPosition; -}; struct Rgba { @@ -26,13 +16,43 @@ struct Rgba uint8_t peBlue; uint8_t peFlags; }; + union ColorRgba { + ColorRgba() = default; + + explicit ColorRgba(uint32_t color) + : Color(color) + { + } + + explicit ColorRgba(Rgba rgba) + : rgba(rgba) + { + } + uint32_t Color; Rgba rgba; }; + static_assert(sizeof(ColorRgba) == 4, "Wrong size of RGBA color"); +struct gdrv_bitmap8 +{ + ColorRgba* BmpBufPtr1; + char* IndexedBmpPtr; + int Width; + int Height; + int Stride; + int IndexedStride; + BitmapTypes BitmapType; + int XPosition; + int YPosition; + unsigned Resolution; + //ColorRgba* RgbaBuffer; + SDL_Texture* Texture; +}; + class gdrv { @@ -42,25 +62,26 @@ public: static int init(int width, int height); static int uninit(); static void get_focus(); - static int create_bitmap(gdrv_bitmap8* bmp, int width, int height); - static int create_raw_bitmap(gdrv_bitmap8* bmp, int width, int height, int flag); - static int create_spliced_bitmap(gdrv_bitmap8* bmp, int width, int height, int size); + static int create_bitmap(gdrv_bitmap8* bmp, int width, int height, int stride = -1, bool indexed = true); + static int create_bitmap(gdrv_bitmap8& bmp, const struct dat8BitBmpHeader& header); static int destroy_bitmap(gdrv_bitmap8* bmp); static int display_palette(ColorRgba* plt); static void start_blit_sequence(); static void end_blit_sequence(); static void blit(gdrv_bitmap8* bmp, int xSrc, int ySrc, int xDest, int yDest, int width, int height); static void blat(gdrv_bitmap8* bmp, int xDest, int yDest); - static void fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, char fillChar); + static void fill_bitmap(gdrv_bitmap8* bmp, int width, int height, int xOff, int yOff, uint8_t fillChar); static void copy_bitmap(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff); static void copy_bitmap_w_transparency(gdrv_bitmap8* dstBmp, int width, int height, int xOff, int yOff, gdrv_bitmap8* srcBmp, int srcXOff, int srcYOff); static void grtext_draw_ttext_in_box(LPCSTR text, int xOff, int yOff, int width, int height, int a6); static void BlitScreen(); + static void ApplyPalette(gdrv_bitmap8& bmp); + static void CreatePreview(gdrv_bitmap8& bmp); private: static SDL_Texture* vScreenTex; - static char* vScreenPixels; + static ColorRgba* vScreenPixels; static int vScreenWidth, vScreenHeight; static ColorRgba current_palette[256]; diff --git a/SpaceCadetPinball/high_score.cpp b/SpaceCadetPinball/high_score.cpp index 026f193..3c64472 100644 --- a/SpaceCadetPinball/high_score.cpp +++ b/SpaceCadetPinball/high_score.cpp @@ -3,6 +3,8 @@ #include "memory.h" #include "options.h" +#include "pinball.h" +#include "score.h" int high_score::dlg_enter_name; int high_score::dlg_score; diff --git a/SpaceCadetPinball/high_score.h b/SpaceCadetPinball/high_score.h index d7fe0ab..ed94096 100644 --- a/SpaceCadetPinball/high_score.h +++ b/SpaceCadetPinball/high_score.h @@ -1,5 +1,4 @@ #pragma once -#include "pinball.h" struct high_score_struct { diff --git a/SpaceCadetPinball/loader.cpp b/SpaceCadetPinball/loader.cpp index f5d55b1..815019c 100644 --- a/SpaceCadetPinball/loader.cpp +++ b/SpaceCadetPinball/loader.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "loader.h" #include "memory.h" -#include "partman.h" +#include "GroupData.h" #include "pb.h" #include "pinball.h" #include "Sound.h" @@ -12,7 +12,7 @@ errorMsg loader::loader_errors[] = { errorMsg{0, "Bad Handle"}, errorMsg{1, "No Type Field"}, - errorMsg{2, "No Attributes Field"}, + errorMsg{2, "No Attributes Field"}, errorMsg{3, "Wrong Type: MATERIAL Expected"}, errorMsg{4, "Wrong Type: KICKER Expected"}, errorMsg{5, "Wrong Type: AN_OBJECT Expected"}, @@ -42,8 +42,8 @@ errorMsg loader::loader_errors[] = int loader::sound_count = 1; int loader::loader_sound_count; -datFileStruct* loader::loader_table; -datFileStruct* loader::sound_record_table; +DatFile* loader::loader_table; +DatFile* loader::sound_record_table; soundListStruct loader::sound_list[65]; int loader::error(int errorCode, int captionCode) @@ -82,14 +82,14 @@ void loader::default_vsi(visualStruct* visual) visual->SoundIndex4 = 0; } -void loader::loadfrom(datFileStruct* datFile) +void loader::loadfrom(DatFile* datFile) { loader_table = datFile; sound_record_table = loader_table; - for (auto groupIndex = 0; groupIndex < datFile->NumberOfGroups; ++groupIndex) + for (auto groupIndex = 0; groupIndex < datFile->Groups.size(); ++groupIndex) { - auto value = reinterpret_cast(partman::field(datFile, groupIndex, datFieldTypes::ShortValue)); + auto value = reinterpret_cast(datFile->field(groupIndex, FieldTypes::ShortValue)); if (value && *value == 202) { if (sound_count < 65) @@ -136,26 +136,27 @@ int loader::get_sound_id(int groupIndex) if (!sound_list[soundIndex].Loaded && !sound_list[soundIndex].WavePtr) { WaveHeader wavHeader{}; - + int soundGroupId = sound_list[soundIndex].GroupIndex; sound_list[soundIndex].Duration = 0.0; if (soundGroupId > 0 && !pinball::quickFlag) { - auto value = reinterpret_cast(partman::field(loader_table, soundGroupId, - datFieldTypes::ShortValue)); + auto value = reinterpret_cast(loader_table->field(soundGroupId, + FieldTypes::ShortValue)); if (value && *value == 202) { - std::string fileName = partman::field(loader_table, soundGroupId, datFieldTypes::String); + std::string fileName = loader_table->field(soundGroupId, FieldTypes::String); // File name is in lower case, while game data is in upper case. - std::transform(fileName.begin(), fileName.end(), fileName.begin(), [](unsigned char c) { return std::toupper(c); }); + std::transform(fileName.begin(), fileName.end(), fileName.begin(), + [](unsigned char c) { return std::toupper(c); }); if (pb::FullTiltMode) { // FT sounds are in SOUND subfolder fileName.insert(0, 1, PathSeparator); fileName.insert(0, "SOUND"); } - + auto filePath = pinball::make_path_name(fileName); auto file = fopen(filePath.c_str(), "rb"); if (file) @@ -163,7 +164,7 @@ int loader::get_sound_id(int groupIndex) fread(&wavHeader, 1, sizeof wavHeader, file); fclose(file); } - + auto sampleCount = wavHeader.data_size / (wavHeader.channels * (wavHeader.bits_per_sample / 8.0)); sound_list[soundIndex].Duration = static_cast(sampleCount / wavHeader.sample_rate); sound_list[soundIndex].WavePtr = Sound::LoadWaveFile(filePath); @@ -178,7 +179,7 @@ int loader::get_sound_id(int groupIndex) int loader::query_handle(LPCSTR lpString) { - return partman::record_labeled(loader_table, lpString); + return loader_table->record_labeled(lpString); } short loader::query_visual_states(int groupIndex) @@ -186,7 +187,7 @@ short loader::query_visual_states(int groupIndex) short result; if (groupIndex < 0) return error(0, 17); - auto shortArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::ShortArray)); + auto shortArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::ShortArray)); if (shortArr && *shortArr == 100) result = shortArr[1]; else @@ -202,7 +203,7 @@ char* loader::query_name(int groupIndex) return nullptr; } - return partman::field(loader_table, groupIndex, datFieldTypes::GroupName); + return loader_table->field(groupIndex, FieldTypes::GroupName); } int16_t* loader::query_iattribute(int groupIndex, int firstValue, int* arraySize) @@ -215,13 +216,13 @@ int16_t* loader::query_iattribute(int groupIndex, int firstValue, int* arraySize for (auto skipIndex = 0;; ++skipIndex) { - auto shortArr = reinterpret_cast(partman::field_nth(loader_table, groupIndex, - datFieldTypes::ShortArray, skipIndex)); + auto shortArr = reinterpret_cast(loader_table->field_nth(groupIndex, + FieldTypes::ShortArray, skipIndex)); if (!shortArr) break; if (*shortArr == firstValue) { - *arraySize = partman::field_size(loader_table, groupIndex, datFieldTypes::ShortArray) / 2 - 1; + *arraySize = loader_table->field_size(groupIndex, FieldTypes::ShortArray) / 2 - 1; return shortArr + 1; } } @@ -248,8 +249,8 @@ float* loader::query_float_attribute(int groupIndex, int groupIndexOffset, int f for (auto skipIndex = 0;; ++skipIndex) { - auto floatArr = reinterpret_cast(partman::field_nth(loader_table, stateId, datFieldTypes::FloatArray, - skipIndex)); + auto floatArr = reinterpret_cast(loader_table->field_nth(stateId, FieldTypes::FloatArray, + skipIndex)); if (!floatArr) break; if (static_cast(floor(*floatArr)) == firstValue) @@ -277,8 +278,8 @@ float loader::query_float_attribute(int groupIndex, int groupIndexOffset, int fi for (auto skipIndex = 0;; ++skipIndex) { - auto floatArr = reinterpret_cast(partman::field_nth(loader_table, stateId, - datFieldTypes::FloatArray, skipIndex)); + auto floatArr = reinterpret_cast(loader_table->field_nth(stateId, + FieldTypes::FloatArray, skipIndex)); if (!floatArr) break; if (static_cast(floor(*floatArr)) == firstValue) @@ -295,16 +296,16 @@ int loader::material(int groupIndex, visualStruct* visual) { if (groupIndex < 0) return error(0, 21); - auto shortArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::ShortValue)); + auto shortArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::ShortValue)); if (!shortArr) return error(1, 21); if (*shortArr != 300) return error(3, 21); - auto floatArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::FloatArray)); + auto floatArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::FloatArray)); if (!floatArr) return error(11, 21); - int floatArrLength = partman::field_size(loader_table, groupIndex, datFieldTypes::FloatArray) / 4; + int floatArrLength = loader_table->field_size(groupIndex, FieldTypes::FloatArray) / 4; for (auto index = 0; index < floatArrLength; index += 2) { switch (static_cast(floor(floatArr[index]))) @@ -339,7 +340,7 @@ int loader::state_id(int groupIndex, int groupIndexOffset) auto visualState = query_visual_states(groupIndex); if (visualState <= 0) return error(12, 24); - auto shortArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::ShortValue)); + auto shortArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::ShortValue)); if (!shortArr) return error(1, 24); if (*shortArr != 200) @@ -350,7 +351,7 @@ int loader::state_id(int groupIndex, int groupIndexOffset) return groupIndex; groupIndex += groupIndexOffset; - shortArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::ShortValue)); + shortArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::ShortValue)); if (!shortArr) return error(1, 24); if (*shortArr != 201) @@ -362,15 +363,15 @@ int loader::kicker(int groupIndex, visualKickerStruct* kicker) { if (groupIndex < 0) return error(0, 20); - auto shortArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::ShortValue)); + auto shortArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::ShortValue)); if (!shortArr) return error(1, 20); if (*shortArr != 400) return error(4, 20); - auto floatArr = reinterpret_cast(partman::field(loader_table, groupIndex, datFieldTypes::FloatArray)); + auto floatArr = reinterpret_cast(loader_table->field(groupIndex, FieldTypes::FloatArray)); if (!floatArr) return error(11, 20); - int floatArrLength = partman::field_size(loader_table, groupIndex, datFieldTypes::FloatArray) / 4; + int floatArrLength = loader_table->field_size(groupIndex, FieldTypes::FloatArray) / 4; if (floatArrLength <= 0) return 0; @@ -421,19 +422,13 @@ int loader::query_visual(int groupIndex, int groupIndexOffset, visualStruct* vis if (stateId < 0) return error(16, 18); - visual->Bitmap = reinterpret_cast(partman::field(loader_table, stateId, datFieldTypes::Bitmap8bit)); - visual->ZMap = reinterpret_cast(partman::field(loader_table, stateId, datFieldTypes::Bitmap16bit) - ); - if (visual->ZMap) - { - visual->ZMap->ZPtr1 = visual->ZMap->ZBuffer; - visual->ZMap->ZPtr2 = visual->ZMap->ZPtr1; - } + visual->Bitmap = loader_table->GetBitmap(stateId); + visual->ZMap = loader_table->GetZMap(stateId); - auto shortArr = reinterpret_cast(partman::field(loader_table, stateId, datFieldTypes::ShortArray)); + auto shortArr = reinterpret_cast(loader_table->field(stateId, FieldTypes::ShortArray)); if (shortArr) { - unsigned int shortArrSize = partman::field_size(loader_table, stateId, datFieldTypes::ShortArray); + unsigned int shortArrSize = loader_table->field_size(stateId, FieldTypes::ShortArray); for (auto index = 0u; index < shortArrSize / 2;) { switch (shortArr[0]) @@ -479,13 +474,13 @@ int loader::query_visual(int groupIndex, int groupIndexOffset, visualStruct* vis if (!visual->CollisionGroup) visual->CollisionGroup = 1; - auto floatArr = reinterpret_cast(partman::field(loader_table, stateId, datFieldTypes::FloatArray)); + auto floatArr = reinterpret_cast(loader_table->field(stateId, FieldTypes::FloatArray)); if (!floatArr) return 0; if (*floatArr != 600.0f) return 0; - visual->FloatArrCount = partman::field_size(loader_table, stateId, datFieldTypes::FloatArray) / 4 / 2 - 2; + visual->FloatArrCount = loader_table->field_size(stateId, FieldTypes::FloatArray) / 4 / 2 - 2; auto floatVal = static_cast(floor(floatArr[1]) - 1.0f); switch (floatVal) { diff --git a/SpaceCadetPinball/loader.h b/SpaceCadetPinball/loader.h index a3c5d41..83a216b 100644 --- a/SpaceCadetPinball/loader.h +++ b/SpaceCadetPinball/loader.h @@ -4,7 +4,7 @@ #include "zdrv.h" -struct datFileStruct; +struct DatFile; struct errorMsg { @@ -89,7 +89,7 @@ public: static void default_vsi(visualStruct* visual); static int get_sound_id(int groupIndex); static void unload(); - static void loadfrom(datFileStruct* datFile); + static void loadfrom(DatFile* datFile); static int query_handle(LPCSTR lpString); static short query_visual_states(int groupIndex); static int material(int groupIndex, visualStruct* visual); @@ -101,10 +101,10 @@ public: static float query_float_attribute(int groupIndex, int groupIndexOffset, int firstValue, float defVal); static int16_t* query_iattribute(int groupIndex, int firstValue, int* arraySize); static float play_sound(int soundIndex); - static datFileStruct* loader_table; + static DatFile* loader_table; private: static errorMsg loader_errors[]; - static datFileStruct* sound_record_table; + static DatFile* sound_record_table; static int sound_count; static int loader_sound_count; static soundListStruct sound_list[65]; diff --git a/SpaceCadetPinball/nudge.cpp b/SpaceCadetPinball/nudge.cpp index 128eb05..5caaed0 100644 --- a/SpaceCadetPinball/nudge.cpp +++ b/SpaceCadetPinball/nudge.cpp @@ -7,6 +7,7 @@ #include "render.h" #include "TBall.h" #include "timer.h" +#include "TPinballTable.h" int nudge::nudged_left; int nudge::nudged_right; diff --git a/SpaceCadetPinball/options.cpp b/SpaceCadetPinball/options.cpp index 1a92db7..d736d64 100644 --- a/SpaceCadetPinball/options.cpp +++ b/SpaceCadetPinball/options.cpp @@ -5,6 +5,7 @@ #include "memory.h" #include "midi.h" #include "pb.h" +#include "pinball.h" #include "resource.h" #include "Sound.h" #include "winmain.h" diff --git a/SpaceCadetPinball/options.h b/SpaceCadetPinball/options.h index e151cdc..4986eb4 100644 --- a/SpaceCadetPinball/options.h +++ b/SpaceCadetPinball/options.h @@ -1,5 +1,5 @@ #pragma once -#include "pinball.h" +#include struct optionsStruct { diff --git a/SpaceCadetPinball/partman.cpp b/SpaceCadetPinball/partman.cpp index 46e7187..84523a7 100644 --- a/SpaceCadetPinball/partman.cpp +++ b/SpaceCadetPinball/partman.cpp @@ -1,6 +1,9 @@ #include "pch.h" #include "partman.h" + +#include "fullscrn.h" #include "gdrv.h" +#include "GroupData.h" #include "memory.h" #include "zdrv.h" @@ -9,7 +12,7 @@ short partman::_field_size[] = 2, -1, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0 }; -datFileStruct* partman::load_records(LPCSTR lpFileName, int resolution, bool fullTiltMode) +DatFile* partman::load_records(LPCSTR lpFileName, bool fullTiltMode) { datFileHeader header{}; dat8BitBmpHeader bmpHeader{}; @@ -18,35 +21,23 @@ datFileStruct* partman::load_records(LPCSTR lpFileName, int resolution, bool ful auto fileHandle = fopen(lpFileName, "rb"); if (fileHandle == nullptr) return nullptr; + fread(&header, 1, sizeof header, fileHandle); if (strcmp("PARTOUT(4.0)RESOURCE", header.FileSignature) != 0) { fclose(fileHandle); return nullptr; } - auto datFile = memory::allocate(); + + auto datFile = new DatFile(); if (!datFile) { fclose(fileHandle); return nullptr; } - if (strlen(header.Description) <= 0) - { - datFile->Description = nullptr; - } - else - { - auto lenOfStr = strlen(header.Description); - auto descriptionBuf = memory::allocate(lenOfStr + 1); - datFile->Description = descriptionBuf; - if (!descriptionBuf) - { - fclose(fileHandle); - memory::free(datFile); - return nullptr; - } - strncpy(descriptionBuf, header.Description, lenOfStr + 1); - } + + datFile->AppName = header.AppName; + datFile->Description = header.Description; if (header.Unknown) { @@ -54,102 +45,75 @@ datFileStruct* partman::load_records(LPCSTR lpFileName, int resolution, bool ful if (!unknownBuf) { fclose(fileHandle); - if (datFile->Description) - memory::free(datFile->Description); - memory::free(datFile); + delete datFile; return nullptr; } fread(unknownBuf, 1, header.Unknown, fileHandle); memory::free(unknownBuf); } - auto groupDataBuf = memory::allocate(header.NumberOfGroups); - datFile->GroupData = groupDataBuf; - if (!groupDataBuf) - { - if (datFile->Description) - memory::free(datFile->Description); - memory::free(datFile); - return nullptr; - } - + datFile->Groups.reserve(header.NumberOfGroups); bool abort = false; for (auto groupIndex = 0; !abort && groupIndex < header.NumberOfGroups; ++groupIndex) { auto entryCount = _lread_char(fileHandle); - auto groupDataSize = entryCount <= 0 ? 0 : entryCount - 1; - auto groupData = memory::allocate(1, sizeof(datEntryData) * groupDataSize); - datFile->GroupData[groupIndex] = groupData; - if (!groupData) - break; + auto groupData = new GroupData(groupIndex); + groupData->ReserveEntries(entryCount); - groupData->EntryCount = 0; for (auto entryIndex = 0; entryIndex < entryCount; ++entryIndex) { - auto entryData = &groupData->Entries[groupData->EntryCount]; - auto entryType = static_cast(_lread_char(fileHandle)); + auto entryData = new EntryData(); + auto entryType = static_cast(_lread_char(fileHandle)); entryData->EntryType = entryType; int fieldSize = _field_size[static_cast(entryType)]; if (fieldSize < 0) fieldSize = _lread_long(fileHandle); + entryData->FieldSize = fieldSize; - if (entryType == datFieldTypes::Bitmap8bit) + if (entryType == FieldTypes::Bitmap8bit) { fread(&bmpHeader, 1, sizeof(dat8BitBmpHeader), fileHandle); - if (bmpHeader.Resolution != resolution && bmpHeader.Resolution != -1) - { - fseek(fileHandle, bmpHeader.Size, SEEK_CUR); - continue; - } + assertm(bmpHeader.Size + sizeof(dat8BitBmpHeader) == fieldSize, "partman: Wrong bitmap field size"); + assertm(bmpHeader.Resolution >= 0 && bmpHeader.Resolution <= 2, + "partman: bitmap resolution out of bounds"); auto bmp = memory::allocate(); entryData->Buffer = reinterpret_cast(bmp); - if (!bmp) + if (!bmp || gdrv::create_bitmap(*bmp, bmpHeader)) { abort = true; break; } - int bmpRez; - if (bmpHeader.IsFlagSet(bmp8Flags::Spliced)) - bmpRez = gdrv::create_spliced_bitmap(bmp, bmpHeader.Width, bmpHeader.Height, bmpHeader.Size); - else if (bmpHeader.IsFlagSet(bmp8Flags::DibBitmap)) - bmpRez = gdrv::create_bitmap(bmp, bmpHeader.Width, bmpHeader.Height); - else - bmpRez = gdrv::create_raw_bitmap(bmp, bmpHeader.Width, bmpHeader.Height, - bmpHeader.IsFlagSet(bmp8Flags::RawBmpUnaligned)); - if (bmpRez) - { - abort = true; - break; - } - fread(bmp->BmpBufPtr1, 1, bmpHeader.Size, fileHandle); - bmp->XPosition = bmpHeader.XPosition; - bmp->YPosition = bmpHeader.YPosition; + fread(bmp->IndexedBmpPtr, 1, bmpHeader.Size, fileHandle); } - else if (entryType == datFieldTypes::Bitmap16bit) + else if (entryType == FieldTypes::Bitmap16bit) { /*Full tilt has extra byte(@0:resolution) in zMap*/ + char zMapResolution = 0; if (fullTiltMode) { - char zMapResolution = _lread_char(fileHandle); + zMapResolution = _lread_char(fileHandle); fieldSize--; - if (zMapResolution != resolution && zMapResolution != -1) - { - fseek(fileHandle, fieldSize, SEEK_CUR); - continue; - } + assertm(zMapResolution >= 0 && zMapResolution <= 2, "partman: zMap resolution out of bounds"); } fread(&zMapHeader, 1, sizeof(dat16BitBmpHeader), fileHandle); int length = fieldSize - sizeof(dat16BitBmpHeader); - auto zmap = memory::allocate(1, length); - zmap->Width = zMapHeader.Width; - zmap->Height = zMapHeader.Height; - zmap->Stride = zMapHeader.Stride; - fread(zmap->ZBuffer, 1, length, fileHandle); - entryData->Buffer = reinterpret_cast(zmap); + auto zMap = memory::allocate(); + zdrv::create_zmap(zMap, zMapHeader.Width, zMapHeader.Height, zMapHeader.Stride); + zMap->Resolution = zMapResolution; + if (zMapHeader.Stride * zMapHeader.Height * 2 == length) + { + fread(zMap->ZPtr1, 1, length, fileHandle); + } + else + { + // 3DPB .dat has zeroed zMap headers, in groups 497 and 498, skip them. + fseek(fileHandle, length, SEEK_CUR); + } + entryData->Buffer = reinterpret_cast(zMap); } else { @@ -163,122 +127,19 @@ datFileStruct* partman::load_records(LPCSTR lpFileName, int resolution, bool ful fread(entryBuffer, 1, fieldSize, fileHandle); } - entryData->FieldSize = fieldSize; - groupData->EntryCount++; + groupData->AddEntry(entryData); } - datFile->NumberOfGroups = groupIndex + 1; + + datFile->Groups.push_back(groupData); } fclose(fileHandle); - if (datFile->NumberOfGroups == header.NumberOfGroups) + if (datFile->Groups.size() == header.NumberOfGroups) return datFile; - unload_records(datFile); + delete datFile; return nullptr; } - -void partman::unload_records(datFileStruct* datFile) -{ - for (auto groupIndex = 0; groupIndex < datFile->NumberOfGroups; ++groupIndex) - { - auto group = datFile->GroupData[groupIndex]; - if (!group) - continue; - - for (auto entryIndex = 0; entryIndex < group->EntryCount; ++entryIndex) - { - auto entry = &group->Entries[entryIndex]; - if (entry->Buffer) - { - if (entry->EntryType == datFieldTypes::Bitmap8bit) - gdrv::destroy_bitmap(reinterpret_cast(entry->Buffer)); - memory::free(entry->Buffer); - } - } - memory::free(group); - } - if (datFile->Description) - memory::free(datFile->Description); - memory::free(datFile->GroupData); - memory::free(datFile); -} - -char* partman::field(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType) -{ - auto group = datFile->GroupData[groupIndex]; - for (auto entryIndex = 0; entryIndex < group->EntryCount; ++entryIndex) - { - auto entry = &group->Entries[entryIndex]; - if (entry->EntryType == targetEntryType) - return entry->Buffer; - if (entry->EntryType > targetEntryType) - break; - } - return nullptr; -} - - -char* partman::field_nth(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType, int skipFirstN) -{ - auto group = datFile->GroupData[groupIndex]; - for (auto skipCount = 0, entryIndex = 0; entryIndex < group->EntryCount; ++entryIndex) - { - auto entry = &group->Entries[entryIndex]; - if (entry->EntryType > targetEntryType) - break; - if (entry->EntryType == targetEntryType) - if (skipCount++ == skipFirstN) - return entry->Buffer; - } - return nullptr; -} - -int partman::field_size_nth(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType, int skipFirstN) -{ - auto group = datFile->GroupData[groupIndex]; - for (auto skipCount = 0, entryIndex = 0; entryIndex < group->EntryCount; ++entryIndex) - { - auto entry = &group->Entries[entryIndex]; - if (entry->EntryType > targetEntryType) - return 0; - if (entry->EntryType == targetEntryType) - if (skipCount++ == skipFirstN) - return entry->FieldSize; - } - return 0; -} - -int partman::field_size(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType) -{ - return field_size_nth(datFile, groupIndex, targetEntryType, 0); -} - -int partman::record_labeled(datFileStruct* datFile, LPCSTR targetGroupName) -{ - auto targetLength = strlen(targetGroupName); - for (int groupIndex = datFile->NumberOfGroups - 1; groupIndex >= 0; --groupIndex) - { - auto groupName = field(datFile, groupIndex, datFieldTypes::GroupName); - if (!groupName) - continue; - - int index; - for (index = 0; index < targetLength; index++) - if (targetGroupName[index] != groupName[index]) - break; - if (index == targetLength && !targetGroupName[index] && !groupName[index]) - return groupIndex; - } - - return -1; -} - -char* partman::field_labeled(datFileStruct* datFile, LPCSTR lpString, datFieldTypes fieldType) -{ - auto groupIndex = record_labeled(datFile, lpString); - return groupIndex < 0 ? nullptr : field(datFile, groupIndex, fieldType); -} - char partman::_lread_char(FILE* file) { char Buffer = 0; diff --git a/SpaceCadetPinball/partman.h b/SpaceCadetPinball/partman.h index 0d9a7cb..c421a2e 100644 --- a/SpaceCadetPinball/partman.h +++ b/SpaceCadetPinball/partman.h @@ -1,29 +1,8 @@ #pragma once -enum class datFieldTypes : int16_t -{ - ShortValue = 0, - //, does not have the 32bits size value, but a 16bits value(see above). - Bitmap8bit = 1, - // 8 bpp bitmap - Unknown2 = 2, - GroupName = 3, - // Group name - Unknown4 = 4, - Palette = 5, - // Palette(1 color is 1 DWORD, only present 1 time in PINBALL.DAT ,with a data size of 1024 bytes for 256 colors.Some colors are 0 ,because their indexes are reserved by Windows.) - Unknown6 = 6, - Unknown7 = 7, - Unknown8 = 8, - String = 9, - // String(content) - ShortArray = 10, - // Array of 16bits integer values - FloatArray = 11, - // Array of 32bits floating point values(collision box, ...) - Bitmap16bit = 12, - // 16 bpp bitmap(Heightmap ? ) -}; +struct zmap_header_type; +struct gdrv_bitmap8; + enum class bmp8Flags : unsigned char { @@ -33,8 +12,7 @@ enum class bmp8Flags : unsigned char }; -#pragma pack(push) -#pragma pack(1) +#pragma pack(push, 1) struct datFileHeader { char FileSignature[21]; @@ -45,34 +23,10 @@ struct datFileHeader int SizeOfBody; unsigned short Unknown; }; -#pragma pack(pop) -static_assert(sizeof(datFileHeader) == 183, "Wrong size of datFileHeader"); -struct datEntryData -{ - datFieldTypes EntryType; - int FieldSize; - char* Buffer; -}; - -struct datGroupData -{ - int16_t EntryCount; - datEntryData Entries[1]; -}; - -struct datFileStruct -{ - unsigned short NumberOfGroups; - char* Description; - datGroupData** GroupData; -}; - -#pragma pack(push) -#pragma pack(1) struct dat8BitBmpHeader { - char Resolution; + uint8_t Resolution; int16_t Width; int16_t Height; int16_t XPosition; @@ -80,18 +34,12 @@ struct dat8BitBmpHeader int Size; bmp8Flags Flags; - bool IsFlagSet(bmp8Flags flag) + bool IsFlagSet(bmp8Flags flag) const { return static_cast(Flags) & static_cast(flag); } }; -#pragma pack(pop) -static_assert(sizeof(dat8BitBmpHeader) == 14, "Wrong size of dat8BitBmpHeader"); - - - -#pragma pack(push, 1) struct dat16BitBmpHeader { int16_t Width; @@ -101,22 +49,17 @@ struct dat16BitBmpHeader int16_t Unknown1_0; int16_t Unknown1_1; }; - #pragma pack(pop) +static_assert(sizeof(dat8BitBmpHeader) == 14, "Wrong size of dat8BitBmpHeader"); +static_assert(sizeof(datFileHeader) == 183, "Wrong size of datFileHeader"); static_assert(sizeof(dat16BitBmpHeader) == 14, "Wrong size of zmap_header_type"); + class partman { public: - static datFileStruct* load_records(LPCSTR lpFileName, int resolution, bool fullTiltMode); - static void unload_records(datFileStruct* datFile); - static char* field_nth(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType, int skipFirstN); - static char* field(datFileStruct* datFile, int groupIndex, datFieldTypes entryType); - static int field_size_nth(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType, int skipFirstN); - static int field_size(datFileStruct* datFile, int groupIndex, datFieldTypes targetEntryType); - static int record_labeled(datFileStruct* datFile, LPCSTR targetGroupName); - static char* field_labeled(datFileStruct* datFile, LPCSTR lpString, datFieldTypes fieldType); + static struct DatFile* load_records(LPCSTR lpFileName, bool fullTiltMode); private: static short _field_size[]; static char _lread_char(FILE* file); diff --git a/SpaceCadetPinball/pb.cpp b/SpaceCadetPinball/pb.cpp index bc04af8..8c7ed43 100644 --- a/SpaceCadetPinball/pb.cpp +++ b/SpaceCadetPinball/pb.cpp @@ -24,9 +24,14 @@ #include "TLightGroup.h" #include "TPlunger.h" #include "TTableLayer.h" +#include "GroupData.h" +#include "partman.h" +#include "score.h" +#include "TPinballTable.h" +#include "TTextBox.h" TPinballTable* pb::MainTable = nullptr; -datFileStruct* pb::record_table = nullptr; +DatFile* pb::record_table = nullptr; int pb::time_ticks = 0, pb::demo_mode = 0, pb::cheat_mode = 0, pb::game_mode = 2, pb::mode_countdown_; float pb::time_now, pb::time_next, pb::ball_speed_limit; high_score_struct pb::highscore_table[5]; @@ -39,7 +44,7 @@ int pb::init() ++memory::critical_allocation; auto dataFilePath = pinball::make_path_name(winmain::DatFileName); - record_table = partman::load_records(dataFilePath.c_str(), fullscrn::GetResolution(), FullTiltMode); + record_table = partman::load_records(dataFilePath.c_str(), FullTiltMode); auto useBmpFont = 0; pinball::get_rc_int(158, &useBmpFont); @@ -49,12 +54,12 @@ int pb::init() if (!record_table) return 1; - auto plt = (ColorRgba*)partman::field_labeled(record_table, "background", datFieldTypes::Palette); + auto plt = (ColorRgba*)record_table->field_labeled("background", FieldTypes::Palette); gdrv::display_palette(plt); - auto backgroundBmp = (gdrv_bitmap8*)partman::field_labeled(record_table, "background", datFieldTypes::Bitmap8bit); - auto cameraInfoId = partman::record_labeled(record_table, "camera_info") + fullscrn::GetResolution(); - auto cameraInfo = (float*)partman::field(record_table, cameraInfoId, datFieldTypes::FloatArray); + auto backgroundBmp = record_table->GetBitmap(record_table->record_labeled("background")); + auto cameraInfoId = record_table->record_labeled("camera_info") + fullscrn::GetResolution(); + auto cameraInfo = (float*)record_table->field(cameraInfoId, FieldTypes::FloatArray); /*Full tilt: table size depends on resolution*/ auto resInfo = &fullscrn::resolution_array[fullscrn::GetResolution()]; @@ -84,7 +89,6 @@ int pb::init() 0, 0); - gdrv::destroy_bitmap(backgroundBmp); loader::loadfrom(record_table); if (pinball::quickFlag) @@ -108,7 +112,7 @@ int pb::uninit() { score::unload_msg_font(); loader::unload(); - partman::unload_records(record_table); + delete record_table; high_score::write(highscore_table); if (MainTable) delete MainTable; @@ -287,8 +291,8 @@ void pb::timed_frame(float timeNow, float timeDelta, bool drawBalls) ball->Acceleration.Y = ball->Speed * ball->Acceleration.Y; maths::vector_add(&ball->Acceleration, &vec2); ball->Speed = maths::normalize_2d(&ball->Acceleration); - ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.X; - ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1000000000.0f : 1.0f / ball->Acceleration.Y; + ball->InvAcceleration.X = ball->Acceleration.X == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.X; + ball->InvAcceleration.Y = ball->Acceleration.Y == 0.0f ? 1.0e9f : 1.0f / ball->Acceleration.Y; } auto timeDelta2 = timeDelta; diff --git a/SpaceCadetPinball/pb.h b/SpaceCadetPinball/pb.h index b78f436..e85d5ee 100644 --- a/SpaceCadetPinball/pb.h +++ b/SpaceCadetPinball/pb.h @@ -1,8 +1,8 @@ #pragma once #include "high_score.h" -#include "partman.h" -#include "TPinballTable.h" +class TPinballTable; +class DatFile; class TBall; class pb @@ -11,7 +11,7 @@ public: static int time_ticks; static float ball_speed_limit, time_now, time_next; static int cheat_mode, game_mode; - static datFileStruct* record_table; + static DatFile* record_table; static TPinballTable* MainTable; static high_score_struct highscore_table[5]; static bool FullTiltMode; diff --git a/SpaceCadetPinball/pinball.h b/SpaceCadetPinball/pinball.h index 3a3d8e9..8b92e50 100644 --- a/SpaceCadetPinball/pinball.h +++ b/SpaceCadetPinball/pinball.h @@ -1,5 +1,6 @@ #pragma once -#include "TTextBox.h" + +class TTextBox; class pinball { diff --git a/SpaceCadetPinball/render.cpp b/SpaceCadetPinball/render.cpp index 72acc52..4668460 100644 --- a/SpaceCadetPinball/render.cpp +++ b/SpaceCadetPinball/render.cpp @@ -1,6 +1,9 @@ #include "pch.h" #include "render.h" + +#include "GroupData.h" #include "memory.h" +#include "pb.h" int render::blit = 0; int render::many_dirty, render::many_sprites, render::many_balls; @@ -21,7 +24,7 @@ void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int h sprite_list = memory::allocate(1000); dirty_list = memory::allocate(1000); ball_list = memory::allocate(20); - gdrv::create_bitmap(&vscreen, width, height); + gdrv::create_bitmap(&vscreen, width, height, width, false); zdrv::create_zmap(&zscreen, width, height); zdrv::fill(&zscreen, zscreen.Width, zscreen.Height, 0, 0, 0xFFFF); vscreen_rect.YPosition = 0; @@ -33,7 +36,7 @@ void render::init(gdrv_bitmap8* bmp, float zMin, float zScaler, int width, int h gdrv_bitmap8* ballBmp = ball_bitmap; while (ballBmp < &ball_bitmap[20]) { - gdrv::create_raw_bitmap(ballBmp, 64, 64, 1); + gdrv::create_bitmap(ballBmp, 64, 64, 64, false); ++ballBmp; } background_bitmap = bmp; @@ -473,7 +476,7 @@ void render::paint_balls() void render::unpaint_balls() { - for (int index = many_balls-1; index >= 0; index--) + for (int index = many_balls - 1; index >= 0; index--) { auto curBall = ball_list[index]; if (curBall->DirtyRect.Width > 0) @@ -554,3 +557,95 @@ void render::build_occlude_list() --memory::critical_allocation; } + +void render::SpriteViewer(bool* show) +{ + static const char* BitmapTypes[] = + { + "None", + "RawBitmap", + "DibBitmap", + "Spliced", + }; + static float scale = 1.0f; + auto uv_min = ImVec2(0.0f, 0.0f); // Top-left + auto uv_max = ImVec2(1.0f, 1.0f); // Lower-right + auto tint_col = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint + auto border_col = ImVec4(1.0f, 1.0f, 1.0f, 0.5f); // 50% opaque white + + if (ImGui::Begin("Sprite viewer", show, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar)) + { + if (ImGui::BeginMenuBar()) + { + ImGui::SliderFloat("Sprite scale", &scale, 0.1f, 10.0f, "scale = %.3f"); + ImGui::EndMenuBar(); + } + + for (const auto group : pb::record_table->Groups) + { + bool emptyGroup = true; + for (int i = 0; i <= 2; i++) + { + auto bmp = group->GetBitmap(i); + if (bmp) + { + emptyGroup = false; + break; + } + } + if (emptyGroup) + continue; + + ImGui::Text("Group: %d, name:%s", group->GroupId, group->GroupName.c_str()); + for (int i = 0; i <= 2; i++) + { + auto bmp = group->GetBitmap(i); + if (!bmp) + continue; + + auto type = BitmapTypes[static_cast(bmp->BitmapType)]; + ImGui::Text("type:%s, size:%d, resolution: %dx%d, offset:%dx%d", type, + bmp->Resolution, + bmp->Width, bmp->Height, bmp->XPosition, bmp->YPosition); + } + + for (int same = 0, i = 0; i <= 2; i++) + { + auto bmp = group->GetBitmap(i); + if (!bmp) + continue; + + gdrv::CreatePreview(*bmp); + if (bmp->Texture) + { + if (!same) + same = true; + else + ImGui::SameLine(); + + ImGui::Image(bmp->Texture, ImVec2(bmp->Width * scale, bmp->Height * scale), + uv_min, uv_max, tint_col, border_col); + } + } + + for (int same = 0, i = 0; i <= 2; i++) + { + auto zMap = group->GetZMap(i); + if (!zMap) + continue; + + zdrv::CreatePreview(*zMap); + if (zMap->Texture) + { + if (!same) + same = true; + else + ImGui::SameLine(); + ImGui::Image(zMap->Texture, ImVec2(zMap->Width * scale, zMap->Height * scale), + uv_min, uv_max, tint_col, border_col); + } + } + } + } + ImGui::End(); +} diff --git a/SpaceCadetPinball/render.h b/SpaceCadetPinball/render.h index 050983e..7075e7b 100644 --- a/SpaceCadetPinball/render.h +++ b/SpaceCadetPinball/render.h @@ -61,4 +61,5 @@ public: static void unpaint_balls(); static void shift(int offsetX, int offsetY, int xSrc, int ySrc, int DestWidth, int DestHeight); static void build_occlude_list(); + static void SpriteViewer(bool* show); }; diff --git a/SpaceCadetPinball/score.cpp b/SpaceCadetPinball/score.cpp index b8be09d..262400d 100644 --- a/SpaceCadetPinball/score.cpp +++ b/SpaceCadetPinball/score.cpp @@ -4,11 +4,9 @@ #include "fullscrn.h" #include "loader.h" #include "memory.h" -#include "partman.h" +#include "GroupData.h" #include "pb.h" #include "render.h" -#include "TDrain.h" -#include "winmain.h" // Todo: load font from file const uint8_t PB_MSGFT_bin[] @@ -32,9 +30,9 @@ scoreStruct* score::create(LPCSTR fieldName, gdrv_bitmap8* renderBgBmp) score->BackgroundBmp = renderBgBmp; /*Full tilt: score box dimensions index is offset by resolution*/ - auto dimensionsId = partman::record_labeled(pb::record_table, fieldName) + fullscrn::GetResolution(); - auto dimensions = reinterpret_cast(partman::field(loader::loader_table, dimensionsId, - datFieldTypes::ShortArray)); + auto dimensionsId = pb::record_table->record_labeled(fieldName) + fullscrn::GetResolution(); + auto dimensions = reinterpret_cast(loader::loader_table->field( dimensionsId, + FieldTypes::ShortArray)); if (!dimensions) { memory::free(score); @@ -48,8 +46,7 @@ scoreStruct* score::create(LPCSTR fieldName, gdrv_bitmap8* renderBgBmp) for (int index = 0; index < 10; index++) { - score->CharBmp[index] = reinterpret_cast(partman::field( - loader::loader_table, groupIndex, datFieldTypes::Bitmap8bit)); + score->CharBmp[index] = loader::loader_table->GetBitmap(groupIndex); ++groupIndex; } return score; @@ -122,7 +119,7 @@ void score::load_msg_font_3DPB(LPCSTR lpName) break; } - if (gdrv::create_raw_bitmap(bmp, width, height, 0)) + if (gdrv::create_bitmap(bmp, width, height, width)) { memory::free(bmp); msg_fontp->Chars[charInd] = nullptr; @@ -133,12 +130,12 @@ void score::load_msg_font_3DPB(LPCSTR lpName) memcpy(tmpCharBur + 3, ptrToData, sizeInBytes); ptrToData += sizeInBytes; - auto srcptr = tmpCharBur + 4; - auto dstPtr = &bmp->BmpBufPtr1[bmp->Stride * (bmp->Height - 1)]; + auto srcPtr = tmpCharBur + 4; + auto dstPtr = &bmp->IndexedBmpPtr[bmp->Stride * (bmp->Height - 1)]; for (auto y = 0; y < height; ++y) { - memcpy(dstPtr, srcptr, width); - srcptr += width; + memcpy(dstPtr, srcPtr, width); + srcPtr += width; dstPtr -= bmp->Stride; } } @@ -152,7 +149,7 @@ void score::load_msg_font_FT(LPCSTR lpName) { if (!pb::record_table) return; - int groupIndex = partman::record_labeled(pb::record_table, lpName); + int groupIndex = pb::record_table->record_labeled(lpName); if (groupIndex < 0) return; msg_fontp = reinterpret_cast(memory::allocate(sizeof(score_msg_font_type))); @@ -160,15 +157,14 @@ void score::load_msg_font_FT(LPCSTR lpName) return; memset(msg_fontp, 0, sizeof(score_msg_font_type)); - auto gapArray = reinterpret_cast(partman::field(pb::record_table, groupIndex, datFieldTypes::ShortArray)); + auto gapArray = reinterpret_cast(pb::record_table->field(groupIndex, FieldTypes::ShortArray)); if (gapArray) msg_fontp->GapWidth = gapArray[fullscrn::GetResolution()]; else msg_fontp->GapWidth = 0; for (auto charIndex = 32; charIndex < 128; charIndex++, ++groupIndex) { - auto bmp = reinterpret_cast(partman::field(pb::record_table, groupIndex, - datFieldTypes::Bitmap8bit)); + auto bmp = pb::record_table->GetBitmap(groupIndex); if (!bmp) break; if (!msg_fontp->Height) @@ -181,7 +177,7 @@ void score::unload_msg_font() { if (msg_fontp) { - /*3DB creates bitmaps, FT just references them from partman*/ + /*3DBP creates bitmaps, FT just references them from partman*/ if (!pb::FullTiltMode) for (int i = 0; i < 128; i++) { @@ -314,3 +310,18 @@ void score::string_format(int score, char* str) } } } + +void score::ApplyPalette() +{ + if (!msg_fontp || pb::FullTiltMode) + return; + + // Only 3DPB font needs this, because it is not loaded by partman + for (auto& Char : msg_fontp->Chars) + { + if (Char) + { + gdrv::ApplyPalette(*Char); + } + } +} diff --git a/SpaceCadetPinball/score.h b/SpaceCadetPinball/score.h index 33bf39e..39d5529 100644 --- a/SpaceCadetPinball/score.h +++ b/SpaceCadetPinball/score.h @@ -42,6 +42,7 @@ public: static void set(scoreStruct* score, int value); static void update(scoreStruct* score); static void string_format(int score, char* str); + static void ApplyPalette(); private : static void load_msg_font_3DPB(LPCSTR lpName); static void load_msg_font_FT(LPCSTR lpName); diff --git a/SpaceCadetPinball/winmain.cpp b/SpaceCadetPinball/winmain.cpp index a3fcb69..1d028e1 100644 --- a/SpaceCadetPinball/winmain.cpp +++ b/SpaceCadetPinball/winmain.cpp @@ -7,6 +7,7 @@ #include "pinball.h" #include "options.h" #include "pb.h" +#include "render.h" #include "Sound.h" #include "resource.h" @@ -36,6 +37,7 @@ gdrv_bitmap8 winmain::gfr_display{}; std::string winmain::DatFileName; bool winmain::ShowAboutDialog = false; bool winmain::ShowImGuiDemo = false; +bool winmain::ShowSpriteViewer = false; bool winmain::LaunchBallEnabled = true; bool winmain::HighScoresEnabled = true; bool winmain::DemoActive = false; @@ -137,7 +139,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) if (pb::init()) { SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Could not load game data", - "The .dat file is missing", window); + "The .dat file is missing", window); return 1; } @@ -180,7 +182,7 @@ int winmain::WinMain(LPCSTR lpCmdLine) char buf[60]; auto elapsedSec = static_cast(curTime - prevTime) * 0.001f; snprintf(buf, sizeof buf, "Updates/sec = %02.02f Frames/sec = %02.02f ", - 300.0f / elapsedSec, frameCounter / elapsedSec); + 300.0f / elapsedSec, frameCounter / elapsedSec); SDL_SetWindowTitle(window, buf); frameCounter = 0; @@ -199,15 +201,14 @@ int winmain::WinMain(LPCSTR lpCmdLine) redGreen = i1; } - auto clr = Rgba{redGreen, redGreen, blue, 0}; - *pltPtr++ = {*reinterpret_cast(&clr)}; + *pltPtr++ = ColorRgba{Rgba{redGreen, redGreen, blue, 0}}; } gdrv::display_palette(plt); free(plt); - gdrv::create_bitmap(&gfr_display, 400, 15); + gdrv::create_bitmap(&gfr_display, 400, 15, 400, false); } - gdrv::blit(&gfr_display, 0, 0, 0, 0, 300, 10); + gdrv::blit(&gfr_display, 0, 0, 0, 30, 300, 10); gdrv::fill_bitmap(&gfr_display, 300, 10, 0, 0, 0); } } @@ -254,12 +255,12 @@ int winmain::WinMain(LPCSTR lpCmdLine) if (gfr_display.BmpBufPtr1) { auto deltaT = now - then + 10; - auto fillChar = static_cast(deltaT); + auto fillChar = static_cast(deltaT); if (deltaT > 236) { - fillChar = -7; + fillChar = 1; } - gdrv::fill_bitmap(&gfr_display, 1, 10, 299 - updateCounter, 0, fillChar); + gdrv::fill_bitmap(&gfr_display, 1, 10, 300 - updateCounter, 0, fillChar); } --updateCounter; then = now; @@ -329,7 +330,7 @@ void winmain::RenderUi() // No demo window in release to save space #ifndef NDEBUG if (ShowImGuiDemo) - ImGui::ShowDemoWindow(); + ImGui::ShowDemoWindow(&ShowImGuiDemo); #endif if (ImGui::BeginMainMenuBar()) @@ -444,6 +445,12 @@ void winmain::RenderUi() ShowImGuiDemo ^= true; } #endif + if (ImGui::MenuItem("Sprite Viewer", nullptr, ShowSpriteViewer)) + { + if (!ShowSpriteViewer && !single_step) + pause(); + ShowSpriteViewer ^= true; + } if (ImGui::MenuItem("Help Topics", "F1")) { if (!single_step) @@ -465,6 +472,8 @@ void winmain::RenderUi() a_dialog(); high_score::RenderHighScoreDialog(); + if (ShowSpriteViewer) + render::SpriteViewer(&ShowSpriteViewer); } int winmain::event_handler(const SDL_Event* event) @@ -553,7 +562,7 @@ int winmain::event_handler(const SDL_Event* event) switch (event->key.keysym.sym) { - case SDLK_h: + case SDLK_g: DispGRhistory = 1; break; case SDLK_y: @@ -627,7 +636,6 @@ int winmain::event_handler(const SDL_Event* event) { case SDL_WINDOWEVENT_FOCUS_GAINED: case SDL_WINDOWEVENT_TAKE_FOCUS: - case SDL_WINDOWEVENT_EXPOSED: case SDL_WINDOWEVENT_SHOWN: activated = 1; Sound::Activate(); diff --git a/SpaceCadetPinball/winmain.h b/SpaceCadetPinball/winmain.h index 5e67135..f62d211 100644 --- a/SpaceCadetPinball/winmain.h +++ b/SpaceCadetPinball/winmain.h @@ -11,7 +11,7 @@ public: static ImGuiIO* ImIO; static bool LaunchBallEnabled; static bool HighScoresEnabled; - static bool DemoActive; + static bool DemoActive; static char* BasePath; static int WinMain(LPCSTR lpCmdLine); @@ -32,6 +32,7 @@ private: static bool restart; static bool ShowAboutDialog; static bool ShowImGuiDemo; + static bool ShowSpriteViewer; static void RenderUi(); }; diff --git a/SpaceCadetPinball/zdrv.cpp b/SpaceCadetPinball/zdrv.cpp index 30a26bf..d8767f1 100644 --- a/SpaceCadetPinball/zdrv.cpp +++ b/SpaceCadetPinball/zdrv.cpp @@ -2,20 +2,18 @@ #include "zdrv.h" #include "memory.h" #include "pb.h" +#include "winmain.h" -int zdrv::create_zmap(zmap_header_type* zmap, int width, int height) +int zdrv::create_zmap(zmap_header_type* zmap, int width, int height, int stride) { - int stride = pad(width); - zmap->Stride = stride; - auto bmpBuf = memory::allocate(height * stride); - zmap->ZPtr1 = bmpBuf; - if (!bmpBuf) - return -1; - zmap->ZPtr2 = bmpBuf; zmap->Width = width; zmap->Height = height; - return 0; + zmap->Stride = stride >= 0 ? stride : pad(width); + zmap->Texture = nullptr; + + zmap->ZPtr1 = memory::allocate(zmap->Stride * zmap->Height); + return zmap->ZPtr1 ? 0 : -1; } int zdrv::pad(int width) @@ -32,20 +30,22 @@ int zdrv::destroy_zmap(zmap_header_type* zmap) return -1; if (zmap->ZPtr1) memory::free(zmap->ZPtr1); + if (zmap->Texture) + SDL_DestroyTexture(zmap->Texture); memset(zmap, 0, sizeof(zmap_header_type)); return 0; } void zdrv::fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, uint16_t fillWord) { - auto dstPtr = &zmap->ZPtr1[zmap->Stride * (zmap->Height - height - yOff) + xOff]; + auto dstPtr = &zmap->ZPtr1[zmap->Stride * yOff + xOff]; for (int y = height; y > 0; --y) { for (int x = width; x > 0; --x) { *dstPtr++ = fillWord; } - dstPtr += zmap->Stride - width; + dstPtr += zmap->Stride - width; } } @@ -54,19 +54,12 @@ void zdrv::paint(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOff, in int dstZMapXOff, int dstZMapYOff, gdrv_bitmap8* srcBmp, int srcBmpXOff, int srcBmpYOff, zmap_header_type* srcZMap, int srcZMapXOff, int srcZMapYOff) { - if (srcBmp->BitmapType == BitmapTypes::Spliced) - { - /*Spliced bitmap is also a zMap, how convenient*/ - paint_spliced_bmp(srcBmp->XPosition, srcBmp->YPosition, dstBmp, dstZMap, srcBmp); - return; - } + assertm(srcBmp->BitmapType != BitmapTypes::Spliced, "Wrong bmp type"); - int dstHeightAbs = abs(dstBmp->Height); - int srcHeightAbs = abs(srcBmp->Height); - auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * (srcHeightAbs - height - srcBmpYOff) + srcBmpXOff]; - auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * (dstHeightAbs - height - dstBmpYOff) + dstBmpXOff]; - auto srcPtrZ = &srcZMap->ZPtr1[srcZMap->Stride * (srcZMap->Height - height - srcZMapYOff) + srcZMapXOff]; - auto dstPtrZ = &dstZMap->ZPtr1[dstZMap->Stride * (dstZMap->Height - height - dstZMapYOff) + dstZMapXOff]; + auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * srcBmpYOff + srcBmpXOff]; + auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * dstBmpYOff + dstBmpXOff]; + auto srcPtrZ = &srcZMap->ZPtr1[srcZMap->Stride * srcZMapYOff + srcZMapXOff]; + auto dstPtrZ = &dstZMap->ZPtr1[dstZMap->Stride * dstZMapYOff + dstZMapXOff]; for (int y = height; y > 0; y--) { @@ -94,17 +87,17 @@ void zdrv::paint_flat(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOf zmap_header_type* zMap, int dstZMapXOff, int dstZMapYOff, gdrv_bitmap8* srcBmp, int srcBmpXOff, int srcBmpYOff, uint16_t depth) { - int dstHeightAbs = abs(dstBmp->Height); - int srcHeightAbs = abs(srcBmp->Height); - auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * (dstHeightAbs - height - dstBmpYOff) + dstBmpXOff]; - auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * (srcHeightAbs - height - srcBmpYOff) + srcBmpXOff]; - auto zPtr = &zMap->ZPtr1[zMap->Stride * (zMap->Height - height - dstZMapYOff) + dstZMapXOff]; + assertm(srcBmp->BitmapType != BitmapTypes::Spliced, "Wrong bmp type"); + + auto dstPtr = &dstBmp->BmpBufPtr1[dstBmp->Stride * dstBmpYOff + dstBmpXOff]; + auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * srcBmpYOff + srcBmpXOff]; + auto zPtr = &zMap->ZPtr1[zMap->Stride * dstZMapYOff + dstZMapXOff]; for (int y = height; y > 0; y--) { for (int x = width; x > 0; --x) { - if (*srcPtr && *zPtr > depth) + if ((*srcPtr).Color && *zPtr > depth) { *dstPtr = *srcPtr; } @@ -119,40 +112,58 @@ void zdrv::paint_flat(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOf } } -void zdrv::paint_spliced_bmp(int xPos, int yPos, gdrv_bitmap8* dstBmp, zmap_header_type* dstZmap, gdrv_bitmap8* srcBmp) +void zdrv::CreatePreview(zmap_header_type& zMap) { - assertm(srcBmp->BitmapType == BitmapTypes::Spliced, "Wrong bmp type"); - int xOffset = xPos - pb::MainTable->XOffset; - int yOffset = dstBmp->Height - srcBmp->Height - (yPos - pb::MainTable->YOffset); - if (yOffset < 0) + if (zMap.Texture) return; - auto bmpDstPtr = &dstBmp->BmpBufPtr1[xOffset + yOffset * dstBmp->Stride]; - auto zMapDstPtr = &dstZmap->ZPtr2[xOffset + yOffset * dstZmap->Stride]; - auto bmpSrcPtr = reinterpret_cast(srcBmp->BmpBufPtr1); + auto tmpBuff = new ColorRgba[zMap.Width * zMap.Height]; - while (true) + ColorRgba color{}; + auto dst = tmpBuff; + auto src = zMap.ZPtr1; + for (auto y = 0; y < zMap.Height; y++) { - auto stride = static_cast(*bmpSrcPtr++); - if (stride < 0) - break; - - /*Stride is in terms of dst stride, hardcoded to match vScreen width in current resolution*/ - zMapDstPtr += stride; - bmpDstPtr += stride; - for (auto count = *bmpSrcPtr++; count; count--) + for (auto x = 0; x < zMap.Width; x++) { - auto depth = *bmpSrcPtr++; - auto charPtr = reinterpret_cast(&bmpSrcPtr); - if (*zMapDstPtr >= depth) - { - *bmpDstPtr = **charPtr; - *zMapDstPtr = depth; - } + auto depth = static_cast((0xffff - *src++) / 0xff); + color.rgba.peRed = depth; + color.rgba.peGreen = depth; + color.rgba.peBlue = depth; - (*charPtr)++; - ++zMapDstPtr; - ++bmpDstPtr; + /*auto depth = static_cast(*src++) /0xffff; + color.rgba.peRed = (1-depth) * 0xff; + color.rgba.peBlue = (depth) * 0xff;*/ + *dst++ = color; } + src += zMap.Stride - zMap.Width; + } + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest"); + auto texture = SDL_CreateTexture + ( + winmain::Renderer, + SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STATIC, + zMap.Width, zMap.Height + ); + SDL_UpdateTexture(texture, nullptr, tmpBuff, zMap.Width * 4); + zMap.Texture = texture; + delete[] tmpBuff; +} + +void zdrv::FlipZMapHorizontally(const zmap_header_type& zMap) +{ + // Flip in-place, iterate over Height/2 lines + auto dst = zMap.ZPtr1; + auto src = zMap.ZPtr1 + zMap.Stride * (zMap.Height - 1); + for (auto y = zMap.Height - 1; y >= zMap.Height / 2; y--) + { + for (auto x = 0; x < zMap.Width; x++) + { + std::swap(*dst++, *src++); + } + dst += zMap.Stride - zMap.Width; + src -= zMap.Stride + zMap.Width; } } diff --git a/SpaceCadetPinball/zdrv.h b/SpaceCadetPinball/zdrv.h index e9c4fd5..fafe2bb 100644 --- a/SpaceCadetPinball/zdrv.h +++ b/SpaceCadetPinball/zdrv.h @@ -6,16 +6,16 @@ struct zmap_header_type int Width; int Height; int Stride; + unsigned Resolution; uint16_t* ZPtr1; - uint16_t* ZPtr2; - uint16_t ZBuffer[1]; + SDL_Texture* Texture; }; class zdrv { public: static int pad(int width); - static int create_zmap(zmap_header_type* zmap, int width, int height); + static int create_zmap(zmap_header_type* zmap, int width, int height, int stride = -1); static int destroy_zmap(zmap_header_type* zmap); static void fill(zmap_header_type* zmap, int width, int height, int xOff, int yOff, uint16_t fillWord); static void paint(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOff, int dstBmpYOff, @@ -24,6 +24,6 @@ public: static void paint_flat(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOff, int dstBmpYOff, zmap_header_type* zMap, int dstZMapXOff, int dstZMapYOff, gdrv_bitmap8* srcBmp, int srcBmpXOff, int srcBmpYOff, uint16_t depth); - static void paint_spliced_bmp(int xPos, int yPos, gdrv_bitmap8* dstBmp, zmap_header_type* dstZmap, - gdrv_bitmap8* srcBmp); + static void CreatePreview(zmap_header_type& zMap); + static void FlipZMapHorizontally(const zmap_header_type& zMap); };