Added loader for Full Tilt .dat files, v1.

Works with some data hacks in lowest resolution.
Seems to work ok, even though BL is still 3DPB.
This commit is contained in:
Muzychenko Andrey 2021-01-31 17:29:53 +03:00
parent 6ff457eb68
commit 49f6132d23
18 changed files with 205 additions and 48 deletions

View file

@ -39,21 +39,21 @@ Type Meaning/comments
9 String (content)
10 Array of 16bits integer values
11 Array of 32bits floating point values (collision box, ...)
12 16 bpp bitmap (Heightmap?)
12 16 bpp bitmap (zMap)
//-- 8bpp bitmap data header --//
+0: Unknown (0) BYTE
+0: Resolution BYTE 0=640x480, 1=800x600, 2=1024x768, -1=Load in all resolutions
+1: Width WORD
+3: Height WORD
+5: X position WORD
+7 Y position WORD
+9: Size of bitmap DWORD
+13: Unknown (1) BYTE
+13: Flags BYTE bit0=Raw bmp align; bit1=DibBitmap, raw when 0; bit2=Spliced bitmap (aka skipline), combines bmp and zMap in RLE-like way
+14: Bitmap data BYTE*(DWORD@+9)
//-- 16bpp bitmap data header --//
//-- 16bpp zMap data header --//
+0: Width WORD
+2: Height WORD
+4: Pitch/2 WORD
@ -62,6 +62,16 @@ Type Meaning/comments
+12: Unknown (80) WORD
+14: Bitmap data BYTE*(DWORD@+9)
//-- 16bpp zMap data header full tilt --//
+0: Resolution BYTE 0=640x480, 1=800x600, 2=1024x768, -1=Load in all resolutions
+1: Width WORD
+3: Height WORD
+5: Pitch/2 WORD
+7: Unknown (0) DWORD
+11: Unknown (0) WORD
+13: Unknown (80) WORD
+15: Bitmap data BYTE*(DWORD@+9)
//-- Pinball 3D remarkable groups --//

View file

@ -31,6 +31,10 @@ TBall::TBall(TPinballTable* table) : TPinballComponent(table, -1, false)
ListBitmap = new objlist_class<gdrv_bitmap8>(0, 4);
auto groupIndex = loader::query_handle("ball");
/*Full tilt hack - ball is ball0*/
if (groupIndex < 0)
groupIndex = loader::query_handle("ball0");
Offset = *loader::query_float_attribute(groupIndex, 0, 500);
auto visualCount = loader::query_visual_states(groupIndex);
auto index = 0;

View file

@ -27,6 +27,12 @@ TFlipper::TFlipper(TPinballTable* table, int groupIndex) : TCollisionComponent(t
auto collMult = *floatArr;
auto bmpCoef2 = *floatArr2;
auto bmpCoef1 = *floatArr3;
/*Full tilt hack: different flipper speed*/
if (bmpCoef2 > 1)
bmpCoef2 = 0.08f;
if (bmpCoef1 > 1)
bmpCoef1 = 0.04f;
auto vecT2 = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 802));
auto vecT1 = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 801));
auto origin = reinterpret_cast<vector_type*>(loader::query_float_attribute(groupIndex, 0, 800));

View file

@ -19,16 +19,8 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
MessageField = 0;
Timer = 0;
BallCapturedFlag = 0;
auto floatArr1 = loader::query_float_attribute(groupIndex, 0, 407);
if (floatArr1)
Unknown3 = *floatArr1;
else
Unknown3 = 0.25;
auto floatArr2 = loader::query_float_attribute(groupIndex, 0, 701);
if (floatArr2)
GravityMult = *floatArr2;
else
GravityMult = 0.5;
Unknown3 = loader::query_float_attribute(groupIndex, 0, 407, 0.25f);
GravityMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
GravityPull = *loader::query_float_attribute(groupIndex, 0, 305);
loader::query_visual(groupIndex, 0, &visual);
@ -38,7 +30,8 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
if (Circle.RadiusSq == 0.0)
Circle.RadiusSq = 0.001f;
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup, reinterpret_cast<vector_type*>(visual.FloatArr),
auto tCircle = new TCircle(this, &ActiveFlag, visual.CollisionGroup,
reinterpret_cast<vector_type*>(visual.FloatArr),
Circle.RadiusSq);
if (tCircle)
{
@ -49,6 +42,10 @@ THole::THole(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
ZSetValue = loader::query_float_attribute(groupIndex, 0, 408)[2];
FieldFlag = static_cast<int>(floor(*loader::query_float_attribute(groupIndex, 0, 1304)));
/*Full tilt hack - FieldFlag should be on*/
if (!FieldFlag)
FieldFlag = 1;
Circle.RadiusSq = visual.FloatArr[2] * visual.FloatArr[2];
circle.RadiusSq = Circle.RadiusSq;
circle.Center.X = Circle.Center.X;

View file

@ -48,6 +48,19 @@ TPinballComponent::TPinballComponent(TPinballTable* table, int groupIndex, bool
zMap = ListZMap->Get(0);
if (ListBitmap)
{
/* Full tilt hack - spliced bitmap includes zMap
* Users access bitmap-zMap in pairs, pad zMap list with 0 for such users
* zdrv does not access zMap when drawing spliced bitmap*/
if (!ListZMap)
{
ListZMap = new objlist_class<zmap_header_type>(0, 4);
for (int index = 0; index < ListBitmap->GetCount(); index++)
{
assertm(ListBitmap->Get(index)->BitmapType == BitmapType::Spliced, "Wrong zMap padding");
ListZMap->Add(visual.ZMap);
}
}
rectangle_type bmp1Rect{}, tmpRect{};
auto rootBmp = ListBitmap->Get(0);
bmp1Rect.XPosition = rootBmp->XPosition - table->XOffset;

View file

@ -22,16 +22,8 @@ TRamp::TRamp(TPinballTable* table, int groupIndex) : TCollisionComponent(table,
loader::query_visual(groupIndex, 0, &visual);
CollisionGroup = visual.CollisionGroup;
auto floatArr1 = loader::query_float_attribute(groupIndex, 0, 701);
if (floatArr1)
BallFieldMult = *floatArr1;
else
BallFieldMult = 0.2f;
auto floatArr2 = loader::query_float_attribute(groupIndex, 0, 1305);
if (floatArr2)
RampFlag1 = static_cast<int>(floor(*floatArr2));
else
RampFlag1 = 0;
BallFieldMult = loader::query_float_attribute(groupIndex, 0, 701, 0.2f);
RampFlag1 = static_cast<int>(loader::query_float_attribute(groupIndex, 0, 1305, 0));
auto floatArr3Plane = loader::query_float_attribute(groupIndex, 0, 1300);
RampPlaneCount = static_cast<int>(floor(*floatArr3Plane));

View file

@ -49,11 +49,12 @@ TTableLayer::TTableLayer(TPinballTable* table): TCollisionComponent(table, -1, f
PinballTable->GravityAnglY = 1.570796f;
}
auto table3 = PinballTable;
GraityDirX = cos(table3->GravityAnglY) * sin(table3->GravityAngleX) * table3->GravityDirVectMult;
GraityDiY = sin(table3->GravityAnglY) * sin(table3->GravityAngleX) * table3->GravityDirVectMult;
GraityDirX = cos(PinballTable->GravityAnglY) * sin(PinballTable->GravityAngleX) * PinballTable->GravityDirVectMult;
GraityDirY = sin(PinballTable->GravityAnglY) * sin(PinballTable->GravityAngleX) * PinballTable->GravityDirVectMult;
auto angleMultArr = loader::query_float_attribute(groupIndex, 0, 701);
if (angleMultArr)
/*Full tilt hack - GraityMult should be 0.2*/
if (angleMultArr && *angleMultArr < 1)
GraityMult = *angleMultArr;
else
GraityMult = 0.2f;
@ -109,7 +110,7 @@ int TTableLayer::FieldEffect(TBall* ball, vector_type* vecDst)
{
vecDst->X = GraityDirX - (0.5f - static_cast<float>(rand()) * 0.00003051850947599719f + ball->Acceleration.X) *
ball->Speed * GraityMult;
vecDst->Y = GraityDiY - ball->Acceleration.Y * ball->Speed * GraityMult;
vecDst->Y = GraityDirY - ball->Acceleration.Y * ball->Speed * GraityMult;
return 1;
}

View file

@ -26,7 +26,7 @@ public:
float Unknown3F;
float Unknown4F;
float GraityDirX;
float GraityDiY;
float GraityDirY;
int Unknown7;
float GraityMult;
field_effect_type Field;

View file

@ -185,6 +185,21 @@ int gdrv::create_raw_bitmap(gdrv_bitmap8* bmp, int width, int height, int flag)
return 0;
}
int gdrv::create_spliced_bitmap(gdrv_bitmap8* bmp, int width, int height, int size)
{
bmp->Dib = nullptr;
bmp->Width = width;
bmp->Stride = width;
bmp->BitmapType = BitmapType::Spliced;
bmp->Height = height;
char* buf = memory::allocate(size);
bmp->BmpBufPtr1 = buf;
if (!buf)
return -1;
bmp->BmpBufPtr2 = bmp->BmpBufPtr1;
return 0;
}
int gdrv::display_palette(PALETTEENTRY* plt)
{

View file

@ -5,6 +5,7 @@ enum class BitmapType : char
None = 0,
RawBitmap = 1,
DibBitmap = 2,
Spliced = 4,
};
struct gdrv_bitmap8
@ -50,6 +51,7 @@ public:
static int create_bitmap_dib(gdrv_bitmap8* bmp, int width, int height);
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 destroy_bitmap(gdrv_bitmap8* bmp);
static int display_palette(PALETTEENTRY* plt);
static UINT start_blit_sequence();

View file

@ -242,6 +242,37 @@ float* loader::query_float_attribute(int groupIndex, int groupIndexOffset, int f
return nullptr;
}
float loader::query_float_attribute(int groupIndex, int groupIndexOffset, int firstValue, float defVal)
{
if (groupIndex < 0)
{
error(0, 22);
return NAN;
}
int stateId = state_id(groupIndex, groupIndexOffset);
if (stateId < 0)
{
error(16, 22);
return NAN;
}
for (auto skipIndex = 0;; ++skipIndex)
{
auto floatArr = reinterpret_cast<float*>(partman::field_nth(loader_table, stateId,
datFieldTypes::FloatArray,skipIndex));
if (!floatArr)
break;
if (static_cast<__int16>(floor(*floatArr)) == firstValue)
return floatArr[1];
}
if (!isnan(defVal))
return defVal;
error(13, 22);
return NAN;
}
int loader::material(int groupIndex, visualStruct* visual)
{
if (groupIndex < 0)

View file

@ -65,6 +65,7 @@ public:
static int query_visual(int groupIndex, int groupIndexOffset, visualStruct* visual);
static char* query_name(int groupIndex);
static float* query_float_attribute(int groupIndex, int groupIndexOffset, int firstValue);
static float query_float_attribute(int groupIndex, int groupIndexOffset, int firstValue, float defVal);
static __int16* query_iattribute(int groupIndex, int firstValue, int* arraySize);
static float play_sound(int soundIndex);
static datFileStruct* loader_table;

View file

@ -9,8 +9,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)
datFileStruct* partman::load_records(LPCSTR lpFileName, int resolution, bool fullTiltMode)
{
_OFSTRUCT ReOpenBuff{};
datFileHeader header{};
@ -86,10 +85,10 @@ datFileStruct* partman::load_records(LPCSTR lpFileName)
if (!groupData)
break;
groupData->EntryCount = entryCount;
datEntryData* entryData = groupData->Entries;
groupData->EntryCount = 0;
for (auto entryIndex = 0; entryIndex < entryCount; ++entryIndex)
{
auto entryData = &groupData->Entries[groupData->EntryCount];
auto entryType = static_cast<datFieldTypes>(_lread_char(fileHandle));
entryData->EntryType = entryType;
@ -100,6 +99,12 @@ datFileStruct* partman::load_records(LPCSTR lpFileName)
if (entryType == datFieldTypes::Bitmap8bit)
{
_hread(fileHandle, &bmpHeader, sizeof(dat8BitBmpHeader));
if (bmpHeader.Resolution != resolution && bmpHeader.Resolution != -1)
{
_llseek(fileHandle, bmpHeader.Size, 1);
continue;
}
auto bmp = reinterpret_cast<gdrv_bitmap8*>(memory::allocate(sizeof(gdrv_bitmap8)));
entryData->Buffer = reinterpret_cast<char*>(bmp);
if (!bmp)
@ -107,10 +112,15 @@ datFileStruct* partman::load_records(LPCSTR lpFileName)
abort = true;
break;
}
if (bmpHeader.IsFlagSet(bmp8Flags::DibBitmap)
? gdrv::create_bitmap(bmp, bmpHeader.Width, bmpHeader.Height)
: gdrv::create_raw_bitmap(bmp, bmpHeader.Width, bmpHeader.Height,
bmpHeader.IsFlagSet(bmp8Flags::RawBmpUnaligned)))
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;
@ -121,6 +131,18 @@ datFileStruct* partman::load_records(LPCSTR lpFileName)
}
else if (entryType == datFieldTypes::Bitmap16bit)
{
/*Full tilt has extra byte(@0:resolution) in zMap*/
if (fullTiltMode)
{
char zMapResolution = _lread_char(fileHandle);
fieldSize--;
if (zMapResolution != resolution && zMapResolution != -1)
{
_llseek(fileHandle, fieldSize, 1);
continue;
}
}
_hread(fileHandle, &zMapHeader, sizeof(dat16BitBmpHeader));
int length = fieldSize - sizeof(dat16BitBmpHeader);
@ -144,9 +166,9 @@ datFileStruct* partman::load_records(LPCSTR lpFileName)
}
entryData->FieldSize = fieldSize;
datFile->NumberOfGroups = groupIndex + 1;
++entryData;
groupData->EntryCount++;
}
datFile->NumberOfGroups = groupIndex + 1;
}
_lclose(fileHandle);

View file

@ -29,6 +29,7 @@ enum class bmp8Flags : unsigned char
{
RawBmpUnaligned = 1 << 0,
DibBitmap = 1 << 1,
Spliced = 1 << 2,
};
@ -71,7 +72,7 @@ struct datFileStruct
#pragma pack(1)
struct dat8BitBmpHeader
{
char Unknown1;
char Resolution;
__int16 Width;
__int16 Height;
__int16 XPosition;
@ -108,7 +109,7 @@ static_assert(sizeof(dat16BitBmpHeader) == 14, "Wrong size of zmap_header_type")
class partman
{
public:
static datFileStruct* load_records(LPCSTR lpFileName);
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);

View file

@ -37,8 +37,9 @@ int pb::init()
++memory::critical_allocation;
lstrcpyA(datFileName, winmain::DatFileName);
//lstrcpyA(datFileName, "cadet.dat");
pinball::make_path_name(dataFilePath, datFileName, 300);
record_table = partman::load_records(dataFilePath);
record_table = partman::load_records(dataFilePath, 0, strstr(datFileName, "cadet"));
auto useBmpFont = 0;
pinball::get_rc_int(158, &useBmpFont);
@ -55,6 +56,10 @@ int pb::init()
auto backgroundBmp = (gdrv_bitmap8*)partman::field_labeled(record_table, "background", datFieldTypes::Bitmap8bit);
auto cameraInfo = (float*)partman::field_labeled(record_table, "camera_info", datFieldTypes::FloatArray);
/*Full tilt hack - table size is hardcoded*/
if (!tableSize)
tableSize = new short[2]{600, 800};
if (cameraInfo)
{
memcpy(&projMat, cameraInfo, sizeof(float) * 4 * 3);

View file

@ -25,4 +25,13 @@
/*Sound uses PlaySound*/
#undef PlaySound
inline size_t pgm_save(int width, int height, char* data, FILE* outfile)
{
size_t n = 0;
n += fprintf(outfile, "P5\n%d %d\n%d\n", width, height, 0xFF);
n += fwrite(data, 1, width * height, outfile);
return n;
}
#endif //PCH_H

View file

@ -1,6 +1,7 @@
#include "pch.h"
#include "zdrv.h"
#include "memory.h"
#include "pb.h"
int zdrv::create_zmap(zmap_header_type* zmap, int width, int height)
@ -59,6 +60,13 @@ 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 == BitmapType::Spliced)
{
/*Spliced bitmap is also a zMap, how convenient*/
paint_spliced_bmp(srcBmp->XPosition, srcBmp->YPosition, dstBmp, dstZMap, srcBmp);
return;
}
int dstHeightAbs = abs(dstBmp->Height);
int srcHeightAbs = abs(srcBmp->Height);
auto srcPtr = &srcBmp->BmpBufPtr1[srcBmp->Stride * (srcHeightAbs - height - srcBmpYOff) + srcBmpXOff];
@ -116,3 +124,41 @@ void zdrv::paint_flat(int width, int height, gdrv_bitmap8* dstBmp, int dstBmpXOf
zPtr += zMap->Stride - width;
}
}
void zdrv::paint_spliced_bmp(int xPos, int yPos, gdrv_bitmap8* dstBmp, zmap_header_type* dstZmap, gdrv_bitmap8* srcBmp)
{
assertm(srcBmp->BitmapType == BitmapType::Spliced, "Wrong bmp type");
int xOffset = xPos - pb::MainTable->XOffset;
int yOffset = dstBmp->Height - srcBmp->Height - (yPos - pb::MainTable->YOffset);
if (yOffset < 0)
return;
auto bmpDstPtr = &dstBmp->BmpBufPtr2[xOffset + yOffset * dstBmp->Stride];
auto zMapDstPtr = &dstZmap->ZPtr2[xOffset + yOffset * dstZmap->Stride];
auto bmpSrcPtr = reinterpret_cast<unsigned short*>(srcBmp->BmpBufPtr2);
while (true)
{
auto stride = static_cast<short>(*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--)
{
auto depth = *bmpSrcPtr++;
auto charPtr = reinterpret_cast<char**>(&bmpSrcPtr);
if (*zMapDstPtr >= depth)
{
*bmpDstPtr = **charPtr;
*zMapDstPtr = depth;
}
(*charPtr)++;
++zMapDstPtr;
++bmpDstPtr;
}
}
}

View file

@ -24,4 +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, unsigned __int16 depth);
static void paint_spliced_bmp(int xPos, int yPos, gdrv_bitmap8* dstBmp, zmap_header_type* dstZmap,
gdrv_bitmap8* srcBmp);
};