diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0a5d92a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root=true + +[*.{h,c,cpp}] +indent_style=tab +indent_size=4 +tab_width=4 +end_of_line=lf +charset=latin1 +trim_trailing_whitespace=true +insert_final_newline=true diff --git a/.gitignore b/.gitignore index fda8650..82fac27 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,8 @@ *.db *.opendb *.ipch +*.iobj +*.ipdb *.pch *.user *.mcp diff --git a/Common/CoreDefs.h b/Common/CoreDefs.h index 7ff9917..5c59708 100644 --- a/Common/CoreDefs.h +++ b/Common/CoreDefs.h @@ -11,6 +11,7 @@ #if PL_IS_CPP11 #define PL_DELETED = delete +#define PL_STATIC_ASSERT(n) static_assert((n), "Static assert failed: " #n) #else #ifndef nullptr #define nullptr 0 @@ -25,10 +26,22 @@ #endif #define PL_DELETED -#endif +template +struct __PL_StaticAssertHelper +{ +}; + +template<> +struct __PL_StaticAssertHelper +{ + int staticAssertFailed; +}; + +#define PL_STATIC_ASSERT(n) ((void)(&static_cast*>(nullptr)->staticAssertFailed)) + +#endif static const size_t PL_SYSTEM_MEMORY_ALIGNMENT = 16; - #endif diff --git a/ConvertResources.bat b/ConvertResources.bat new file mode 100644 index 0000000..bf1c423 --- /dev/null +++ b/ConvertResources.bat @@ -0,0 +1,25 @@ +x64\Release\MiniRez.exe "GpApp2\Glider PRO.r" Packaged\ApplicationResources.gpr +x64\Release\hqx2gp.exe "GpApp2\Houses\Art Museum.binhex" "Packaged\Houses\Art Museum" +x64\Release\hqx2gp.exe "GpApp2\Houses\California or Bust!.binhex" "Packaged\Houses\California or Bust!" +x64\Release\hqx2gp.exe "GpApp2\Houses\Castle o' the Air.binhex" "Packaged\Houses\Castle o' the Air" +x64\Release\hqx2gp.exe "GpApp2\Houses\CD Demo House.binhex" "Packaged\Houses\CD Demo House" +x64\Release\hqx2gp.exe "GpApp2\Houses\Davis Station.binhex" "Packaged\Houses\Davis Station" +x64\Release\hqx2gp.exe "GpApp2\Houses\Demo House.binhex" "Packaged\Houses\Demo House" +x64\Release\hqx2gp.exe "GpApp2\Houses\Empty House.binhex" "Packaged\Houses\Empty House" +x64\Release\hqx2gp.exe "GpApp2\Houses\Fun House.binhex" "Packaged\Houses\Fun House" +x64\Release\hqx2gp.exe "GpApp2\Houses\Grand Prix.binhex" "Packaged\Houses\Grand Prix" +x64\Release\hqx2gp.exe "GpApp2\Houses\ImagineHouse PRO II.binhex" "Packaged\Houses\ImagineHouse PRO II" +x64\Release\hqx2gp.exe "GpApp2\Houses\In The Mirror.binhex" "Packaged\Houses\In The Mirror" +x64\Release\hqx2gp.exe "GpApp2\Houses\Land of Illusion.binhex" "Packaged\Houses\Land of Illusion" +x64\Release\hqx2gp.exe "GpApp2\Houses\Leviathan.binhex" "Packaged\Houses\Leviathan" +x64\Release\hqx2gp.exe "GpApp2\Houses\Metropolis.binhex" "Packaged\Houses\Metropolis" +x64\Release\hqx2gp.exe "GpApp2\Houses\Nemo's Market.binhex" "Packaged\Houses\Nemo's Market" +x64\Release\hqx2gp.exe "GpApp2\Houses\Rainbow's End.binhex" "Packaged\Houses\Rainbow's End" +x64\Release\hqx2gp.exe "GpApp2\Houses\Sampler.binhex" "Packaged\Houses\Sampler" +x64\Release\hqx2gp.exe "GpApp2\Houses\Slumberland.binhex" "Packaged\Houses\Slumberland" +x64\Release\hqx2gp.exe "GpApp2\Houses\SpacePods.binhex" "Packaged\Houses\SpacePods" +x64\Release\hqx2gp.exe "GpApp2\Houses\Teddy World.binhex" "Packaged\Houses\Teddy World" +x64\Release\hqx2gp.exe "GpApp2\Houses\The Asylum Pro.binhex" "Packaged\Houses\The Asylum Pro" +x64\Release\hqx2gp.exe "GpApp2\Houses\Titanic.binhex" "Packaged\Houses\Titanic" +copy /Y "GpApp2\Houses\*.mov" "Packaged\Houses" +pause diff --git a/GlidePort.sln b/GlidePort.sln index 8dc0ad4..3ccb95f 100644 --- a/GlidePort.sln +++ b/GlidePort.sln @@ -15,6 +15,10 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpD3D", "GpD3D\GpD3D.vcxpro EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hqx2gp", "hqx2gp\hqx2gp.vcxproj", "{5FDE4822-C771-46A5-B6B2-FD12BACE86BF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PictChecker", "PictChecker\PictChecker.vcxproj", "{99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GpAudioDriver_XAudio2", "GpAudioDriver_XAudio2\GpAudioDriver_XAudio2.vcxproj", "{E3BDC783-8646-433E-ADF0-8B6390D36669}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -71,6 +75,22 @@ Global {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x64.Build.0 = Release|x64 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x86.ActiveCfg = Release|Win32 {5FDE4822-C771-46A5-B6B2-FD12BACE86BF}.Release|x86.Build.0 = Release|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.ActiveCfg = Debug|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x64.Build.0 = Debug|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x86.ActiveCfg = Debug|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Debug|x86.Build.0 = Debug|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.ActiveCfg = Release|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x64.Build.0 = Release|x64 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x86.ActiveCfg = Release|Win32 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C}.Release|x86.Build.0 = Release|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.ActiveCfg = Debug|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x64.Build.0 = Debug|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x86.ActiveCfg = Debug|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Debug|x86.Build.0 = Debug|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x64.ActiveCfg = Release|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x64.Build.0 = Release|x64 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x86.ActiveCfg = Release|Win32 + {E3BDC783-8646-433E-ADF0-8B6390D36669}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GpApp/AnimCursor.cpp b/GpApp/AnimCursor.cpp index a8cd342..a295f9d 100644 --- a/GpApp/AnimCursor.cpp +++ b/GpApp/AnimCursor.cpp @@ -221,7 +221,7 @@ void IncrementCursor (void) } } else - SetCursor((CursPtr)*GetCursor(watchCursor)); + SetBuiltinCursor(watchCursor); } //-------------------------------------------------------------- DecrementCursor @@ -249,7 +249,7 @@ void DecrementCursor (void) } } else - SetCursor((CursPtr)*GetCursor(watchCursor)); + SetBuiltinCursor(watchCursor); } //-------------------------------------------------------------- SpinCursor diff --git a/GpApp/Environ.cpp b/GpApp/Environ.cpp index 6f45c19..eb03bb6 100644 --- a/GpApp/Environ.cpp +++ b/GpApp/Environ.cpp @@ -207,7 +207,22 @@ Boolean DoWeHaveDragManager (void) short WhatsOurDepth (void) { - return 8; + PortabilityLayer::PixelFormat pixelFormat; + PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(nil, nil, &pixelFormat); + + switch (pixelFormat) + { + case PortabilityLayer::PixelFormat_8BitCustom: + case PortabilityLayer::PixelFormat_8BitStandard: + return 8; + case PortabilityLayer::PixelFormat_RGB555: + return 16; + case PortabilityLayer::PixelFormat_RGB24: + case PortabilityLayer::PixelFormat_RGB32: + return 32; + default: + return 0; + } } void SwitchToDepth (short, Boolean) @@ -476,7 +491,7 @@ void GetDeviceRect(Rect *rect) { unsigned int width; unsigned int height; - PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(width, height); + PortabilityLayer::HostDisplayDriver::GetInstance()->GetDisplayResolution(&width, &height, nil); SetRect(rect, 0, 0, static_cast(width), static_cast(height)); } diff --git a/GpApp/Externs.h b/GpApp/Externs.h index 0fb58ab..a558609 100644 --- a/GpApp/Externs.h +++ b/GpApp/Externs.h @@ -16,7 +16,7 @@ #define kNilPointer 0L -#define kPutInFront (WindowPtr)-1L +#define kPutInFront (PL_GetPutInFrontWindowPtr()) #define kNormalUpdates TRUE #define kOneKilobyte 1024 #define kOkayButton 1 @@ -387,4 +387,4 @@ void CloseThisWindow (WindowPtr *); #include "GliderDefines.h" #include "GliderStructs.h" #include "GliderVars.h" -#include "GliderProtos.h" \ No newline at end of file +#include "GliderProtos.h" diff --git a/GpApp/GpAppInterface.cpp b/GpApp/GpAppInterface.cpp index cf166ab..9e4fa62 100644 --- a/GpApp/GpAppInterface.cpp +++ b/GpApp/GpAppInterface.cpp @@ -1,57 +1,65 @@ -#include "GpAppInterface.h" - -#include "DisplayDeviceManager.h" -#include "HostFileSystem.h" -#include "HostDisplayDriver.h" -#include "HostSystemServices.h" - -int gpAppMain(); - -class GpAppInterfaceImpl final : public GpAppInterface -{ -public: - int ApplicationMain() override; - void PL_IncrementTickCounter(uint32_t count) override; - void PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) override; - void PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) override; - void PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) override; - void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) override; -}; - - -int GpAppInterfaceImpl::ApplicationMain() -{ - return gpAppMain(); -} - -void GpAppInterfaceImpl::PL_IncrementTickCounter(uint32_t count) -{ - PortabilityLayer::DisplayDeviceManager::GetInstance()->IncrementTickCount(count); -} - -void GpAppInterfaceImpl::PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) -{ - PortabilityLayer::HostFileSystem::SetInstance(instance); -} - -void GpAppInterfaceImpl::PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) -{ - PortabilityLayer::HostDisplayDriver::SetInstance(instance); -} - -void GpAppInterfaceImpl::PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) -{ - PortabilityLayer::HostSystemServices::SetInstance(instance); -} - -void GpAppInterfaceImpl::PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) -{ - PortabilityLayer::InstallHostSuspendHook(hook, context); -} - -static GpAppInterfaceImpl gs_application; - -extern "C" GpAppInterface *GpAppInterface_Get() -{ - return &gs_application; -} +#include "GpAppInterface.h" + +#include "DisplayDeviceManager.h" +#include "HostAudioDriver.h" +#include "HostFileSystem.h" +#include "HostDisplayDriver.h" +#include "HostSystemServices.h" + +int gpAppMain(); + +class GpAppInterfaceImpl final : public GpAppInterface +{ +public: + int ApplicationMain() override; + void PL_IncrementTickCounter(uint32_t count) override; + void PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) override; + void PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) override; + void PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) override; + void PL_HostAudioDriver_SetInstance(PortabilityLayer::HostAudioDriver *instance) override; + void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) override; +}; + + +int GpAppInterfaceImpl::ApplicationMain() +{ + return gpAppMain(); +} + +void GpAppInterfaceImpl::PL_IncrementTickCounter(uint32_t count) +{ + PortabilityLayer::DisplayDeviceManager::GetInstance()->IncrementTickCount(count); +} + +void GpAppInterfaceImpl::PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) +{ + PortabilityLayer::HostFileSystem::SetInstance(instance); +} + +void GpAppInterfaceImpl::PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) +{ + PortabilityLayer::HostDisplayDriver::SetInstance(instance); +} + +void GpAppInterfaceImpl::PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) +{ + PortabilityLayer::HostSystemServices::SetInstance(instance); +} + +void GpAppInterfaceImpl::PL_HostAudioDriver_SetInstance(PortabilityLayer::HostAudioDriver *instance) +{ + PortabilityLayer::HostAudioDriver::SetInstance(instance); +} + +void GpAppInterfaceImpl::PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) +{ + PortabilityLayer::InstallHostSuspendHook(hook, context); +} + +static GpAppInterfaceImpl gs_application; + + +GP_APP_DLL_EXPORT_API GpAppInterface *GpAppInterface_Get() +{ + return &gs_application; +} diff --git a/GpApp/HouseInfo.cpp b/GpApp/HouseInfo.cpp index 67d7301..07f3594 100644 --- a/GpApp/HouseInfo.cpp +++ b/GpApp/HouseInfo.cpp @@ -37,7 +37,6 @@ Rect houseEditText1, houseEditText2; short houseCursorIs; Boolean keyHit, tempPhoneBit; -extern Cursor beamCursor; extern Boolean noRoomAtAll, changeLockStateOfHouse, saveHouseLocked; extern Boolean phoneBitSet; @@ -185,7 +184,7 @@ Boolean HouseFilter (DialogPtr dial, EventRecord *event, short *item) { if (houseCursorIs != kBeamCursor) { - SetCursor(&beamCursor); + SetBuiltinCursor(iBeamCursor); houseCursorIs = kBeamCursor; } } diff --git a/GpApp/InterfaceInit.cpp b/GpApp/InterfaceInit.cpp index 0ca097c..28e301a 100644 --- a/GpApp/InterfaceInit.cpp +++ b/GpApp/InterfaceInit.cpp @@ -23,9 +23,9 @@ extern RgnHandle mirrorRgn; extern WindowPtr mapWindow, toolsWindow, linkWindow; extern WindowPtr menuWindow; extern Rect shieldRect, boardSrcRect, localRoomsDest[]; -extern CursHandle handCursorH, beamCursorH, vertCursorH, horiCursorH; +extern CursHandle handCursorH, vertCursorH, horiCursorH; extern CursHandle diagCursorH; -extern Cursor handCursor, beamCursor, vertCursor, horiCursor; +extern Cursor handCursor, vertCursor, horiCursor; extern Cursor diagCursor; extern MenuHandle appleMenu, gameMenu, optionsMenu, houseMenu; extern Point shieldPt; @@ -85,12 +85,6 @@ void GetExtraCursors (void) HLock((Handle)handCursorH); handCursor = **handCursorH; - beamCursorH = GetCursor(iBeamCursor); - if (beamCursorH == nil) - RedAlert(kErrFailedResourceLoad); - HLock((Handle)beamCursorH); - beamCursor = **beamCursorH; - vertCursorH = GetCursor(kVertCursorID); if (vertCursorH == nil) RedAlert(kErrFailedResourceLoad); diff --git a/GpApp/MainWindow.cpp b/GpApp/MainWindow.cpp index 88f2b46..4a8612e 100644 --- a/GpApp/MainWindow.cpp +++ b/GpApp/MainWindow.cpp @@ -29,9 +29,9 @@ CTabHandle theCTab; PixMapHandle thePMap; ColorSpec * wasColors; ColorSpec * newColors; -CursHandle handCursorH, beamCursorH, vertCursorH, horiCursorH; +CursHandle handCursorH, vertCursorH, horiCursorH; CursHandle diagCursorH; -Cursor handCursor, beamCursor, vertCursor, horiCursor; +Cursor handCursor, vertCursor, horiCursor; Cursor diagCursor; Rect workSrcRect; GWorldPtr workSrcMap; diff --git a/GpApp/Music.cpp b/GpApp/Music.cpp index f6d9962..d622c19 100644 --- a/GpApp/Music.cpp +++ b/GpApp/Music.cpp @@ -1,4 +1,5 @@ +#define _CRT_SECURE_NO_WARNINGS //============================================================================ //---------------------------------------------------------------------------- // Music.c @@ -235,11 +236,11 @@ OSErr LoadMusicSounds (void) HLock(theSound); soundDataSize = GetHandleSize(theSound) - 20L; HUnlock(theSound); - + theMusicData[i] = NewPtr(soundDataSize); if (theMusicData[i] == nil) return (MemError()); - + HLock(theSound); BlockMove((Ptr)(static_cast(*theSound) + 20L), theMusicData[i], soundDataSize); ReleaseResource(theSound); diff --git a/GpApp/ObjectDraw2.cpp b/GpApp/ObjectDraw2.cpp index f03ebd8..e7d4c6f 100644 --- a/GpApp/ObjectDraw2.cpp +++ b/GpApp/ObjectDraw2.cpp @@ -1147,7 +1147,7 @@ void DrawCalendar (Rect *theRect) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&bounds, -bounds.left, -bounds.top); QOffsetRect(&bounds, theRect->left, theRect->top); @@ -1183,7 +1183,7 @@ void DrawBulletin (Rect *theRect) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&bounds, -bounds.left, -bounds.top); QOffsetRect(&bounds, theRect->left, theRect->top); diff --git a/GpApp/ObjectEdit.cpp b/GpApp/ObjectEdit.cpp index f9da849..5a8ca5a 100644 --- a/GpApp/ObjectEdit.cpp +++ b/GpApp/ObjectEdit.cpp @@ -2289,7 +2289,7 @@ void GetThisRoomsObjRects (void) else { HLock((Handle)thePict); - roomObjectRects[i] = (*thePict)->picFrame; + roomObjectRects[i] = (*thePict)->picFrame.ToRect(); HUnlock((Handle)thePict); } ZeroRectCorner(&roomObjectRects[i]); diff --git a/GpApp/ObjectRects.cpp b/GpApp/ObjectRects.cpp index e3f8ab1..8d2c444 100644 --- a/GpApp/ObjectRects.cpp +++ b/GpApp/ObjectRects.cpp @@ -227,7 +227,7 @@ void GetObjectRect (objectPtr who, Rect *itsRect) else { HLock((Handle)thePict); - *itsRect = (*thePict)->picFrame; + *itsRect = (*thePict)->picFrame.ToRect(); HUnlock((Handle)thePict); } ZeroRectCorner(itsRect); diff --git a/GpApp/Play.cpp b/GpApp/Play.cpp index f5a87d3..5a44558 100644 --- a/GpApp/Play.cpp +++ b/GpApp/Play.cpp @@ -532,7 +532,7 @@ void PlayGame (void) if (!thePicture) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&bounds, -bounds.left, -bounds.top); QOffsetRect(&bounds, hOffset, 0); @@ -583,7 +583,7 @@ void PlayGame (void) if (!thePicture) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&bounds, -bounds.left, -bounds.top); QOffsetRect(&bounds, hOffset, 0); diff --git a/GpApp/Room.cpp b/GpApp/Room.cpp index a72e742..2c36a91 100644 --- a/GpApp/Room.cpp +++ b/GpApp/Room.cpp @@ -279,7 +279,7 @@ void ReadyBackground (short theID, short *theTiles) } HLock((Handle)thePicture); - dest = (*thePicture)->picFrame; + dest = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&dest, -dest.left, -dest.top); DrawPicture(thePicture, &dest); diff --git a/GpApp/RoomGraphics.cpp b/GpApp/RoomGraphics.cpp index 724865d..def2a67 100644 --- a/GpApp/RoomGraphics.cpp +++ b/GpApp/RoomGraphics.cpp @@ -148,7 +148,7 @@ void LoadGraphicSpecial (short resID) } HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); OffsetRect(&bounds, -bounds.left, -bounds.top); DrawPicture(thePicture, &bounds); diff --git a/GpApp/RoomInfo.cpp b/GpApp/RoomInfo.cpp index 36cbb39..db6d9b8 100644 --- a/GpApp/RoomInfo.cpp +++ b/GpApp/RoomInfo.cpp @@ -54,7 +54,7 @@ short tileOver, tempBack, cursorIs; Boolean originalLeftOpen, originalTopOpen, originalRightOpen, originalBottomOpen; Boolean originalFloor; -extern Cursor handCursor, beamCursor; +extern Cursor handCursor; extern short houseResFork, lastBackground; @@ -278,7 +278,7 @@ void HiliteTileOver (Point mouseIs) { if (cursorIs != kBeamCursor) { - SetCursor(&beamCursor); + SetBuiltinCursor(iBeamCursor); cursorIs = kBeamCursor; } } diff --git a/GpApp/StructuresInit.cpp b/GpApp/StructuresInit.cpp index 9851222..cf322e3 100644 --- a/GpApp/StructuresInit.cpp +++ b/GpApp/StructuresInit.cpp @@ -82,7 +82,7 @@ void InitScoreboardMap (void) if (thePicture == nil) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); QOffsetRect(&bounds, -bounds.left, -bounds.top); QOffsetRect(&bounds, hOffset, 0); diff --git a/GpApp/Utilities.cpp b/GpApp/Utilities.cpp index a05bf67..314a948 100644 --- a/GpApp/Utilities.cpp +++ b/GpApp/Utilities.cpp @@ -301,7 +301,7 @@ void LoadGraphic (short resID) RedAlert(kErrFailedGraphicLoad); HLock((Handle)thePicture); - bounds = (*thePicture)->picFrame; + bounds = (*thePicture)->picFrame.ToRect(); HUnlock((Handle)thePicture); OffsetRect(&bounds, -bounds.left, -bounds.top); DrawPicture(thePicture, &bounds); diff --git a/GpApp2/Houses/Slumberland.bin b/GpApp2/Houses/Slumberland.bin deleted file mode 100644 index 0de5225..0000000 Binary files a/GpApp2/Houses/Slumberland.bin and /dev/null differ diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp new file mode 100644 index 0000000..754a1f2 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.cpp @@ -0,0 +1,108 @@ +#include "GpAudioChannelXAudio2.h" +#include "GpAudioDriverXAudio2.h" +#include "IGpAudioChannelCallbacks.h" + +#include +#include + +GpAudioChannelXAudio2 *GpAudioChannelXAudio2::Create(GpAudioDriverXAudio2 *driver) +{ + void *storage = malloc(sizeof(GpAudioChannelXAudio2)); + if (!storage) + return nullptr; + + GpAudioChannelXAudio2 *channel = new (storage) GpAudioChannelXAudio2(driver); + if (!channel->Init()) + { + channel->Destroy(); + return nullptr; + } + + return channel; +} + +bool GpAudioChannelXAudio2::Init() +{ + const unsigned int sampleRate = m_driver->GetRealSampleRate(); + IXAudio2 *const xa2 = m_driver->GetXA2(); + + WAVEFORMATEX format; + ZeroMemory(&format, sizeof(format)); + + format.wFormatTag = WAVE_FORMAT_PCM; + format.nChannels = 1; + format.nSamplesPerSec = sampleRate; + format.wBitsPerSample = 8; + format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8; + format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec; + + XAUDIO2_SEND_DESCRIPTOR sendsList[1]; + sendsList[0].Flags = 0; + sendsList[0].pOutputVoice = m_driver->GetMasteringVoice(); + + XAUDIO2_VOICE_SENDS sends; + sends.pSends = sendsList; + sends.SendCount = sizeof(sendsList) / sizeof(sendsList[0]); + + XAUDIO2_VOICE_DETAILS dets; + m_driver->GetMasteringVoice()->GetVoiceDetails(&dets); + + HRESULT hr = xa2->CreateSourceVoice(&m_sourceVoice, &format, XAUDIO2_VOICE_NOPITCH | XAUDIO2_VOICE_NOSRC, 1.0f, &m_xAudioCallbacks, nullptr, nullptr); + if (hr != S_OK) + return false; + + return true; +} + +void GpAudioChannelXAudio2::SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) +{ + m_contextCallbacks = callbacks; +} + +void GpAudioChannelXAudio2::PostBuffer(const void *buffer, size_t bufferSize) +{ + XAUDIO2_BUFFER xa2Buffer; + xa2Buffer.Flags = 0; + xa2Buffer.AudioBytes = static_cast(bufferSize); + xa2Buffer.pAudioData = static_cast(buffer); + xa2Buffer.PlayBegin = 0; + xa2Buffer.PlayLength = 0; + xa2Buffer.LoopBegin = 0; + xa2Buffer.LoopLength = 0; + xa2Buffer.LoopCount = 0; + xa2Buffer.pContext = nullptr; + + m_sourceVoice->SubmitSourceBuffer(&xa2Buffer, nullptr); + if (m_voiceState == VoiceState_Idle) + { + m_voiceState = VoiceState_Active; + m_sourceVoice->Start(0, 0); + } +} + +void GpAudioChannelXAudio2::Destroy() +{ + this->~GpAudioChannelXAudio2(); + free(this); +} + +void GpAudioChannelXAudio2::OnBufferEnd() +{ + if (m_contextCallbacks) + m_contextCallbacks->NotifyBufferFinished(); +} + +GpAudioChannelXAudio2::GpAudioChannelXAudio2(GpAudioDriverXAudio2 *driver) + : m_driver(driver) + , m_xAudioCallbacks(this) + , m_sourceVoice(nullptr) + , m_contextCallbacks(nullptr) + , m_voiceState(VoiceState_Idle) +{ +} + +GpAudioChannelXAudio2::~GpAudioChannelXAudio2() +{ + if (m_sourceVoice) + m_sourceVoice->DestroyVoice(); +} diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h new file mode 100644 index 0000000..cfcfb4c --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2.h @@ -0,0 +1,42 @@ +#pragma once + +#include "IGpAudioChannel.h" + +#include "GpAudioChannelXAudio2Callbacks.h" + +class GpAudioDriverXAudio2; +class GpAudioChannelXAudio2Callbacks; +struct IXAudio2SourceVoice; + +class GpAudioChannelXAudio2 final : public IGpAudioChannel +{ +public: + friend class GpAudioChannelXAudio2Callbacks; + + static GpAudioChannelXAudio2 *Create(GpAudioDriverXAudio2 *driver); + + void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks); + void PostBuffer(const void *buffer, size_t bufferSize); + void Destroy() override; + + bool Init(); + +protected: + void OnBufferEnd(); + +private: + enum VoiceState + { + VoiceState_Idle, + VoiceState_Active, + }; + + explicit GpAudioChannelXAudio2(GpAudioDriverXAudio2 *driver); + ~GpAudioChannelXAudio2(); + + GpAudioDriverXAudio2 *m_driver; + IXAudio2SourceVoice *m_sourceVoice; + GpAudioChannelXAudio2Callbacks m_xAudioCallbacks; + IGpAudioChannelCallbacks *m_contextCallbacks; + VoiceState m_voiceState; +}; diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp new file mode 100644 index 0000000..0291043 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.cpp @@ -0,0 +1,36 @@ +#include "GpAudioChannelXAudio2Callbacks.h" +#include "GpAudioChannelXAudio2.h" + +GpAudioChannelXAudio2Callbacks::GpAudioChannelXAudio2Callbacks(GpAudioChannelXAudio2 *owner) + : m_owner(owner) +{ +} + +void GpAudioChannelXAudio2Callbacks::OnVoiceProcessingPassStart(UINT32 BytesRequired) +{ +} + +void GpAudioChannelXAudio2Callbacks::OnVoiceProcessingPassEnd() +{ +} + +void GpAudioChannelXAudio2Callbacks::OnStreamEnd() +{ +} + +void GpAudioChannelXAudio2Callbacks::OnBufferStart(void* pBufferContext) +{ +} + +void GpAudioChannelXAudio2Callbacks::OnBufferEnd(void* pBufferContext) +{ + m_owner->OnBufferEnd(); +} + +void GpAudioChannelXAudio2Callbacks::OnLoopEnd(void* pBufferContext) +{ +} + +void GpAudioChannelXAudio2Callbacks::OnVoiceError(void* pBufferContext, HRESULT Error) +{ +} diff --git a/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.h b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.h new file mode 100644 index 0000000..f122f44 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioChannelXAudio2Callbacks.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class GpAudioChannelXAudio2; + +class GpAudioChannelXAudio2Callbacks final : public IXAudio2VoiceCallback +{ +public: + explicit GpAudioChannelXAudio2Callbacks(GpAudioChannelXAudio2 *owner); + + void OnVoiceProcessingPassStart(UINT32 BytesRequired) override; + void OnVoiceProcessingPassEnd() override; + void OnStreamEnd() override; + void OnBufferStart(void* pBufferContext) override; + void OnBufferEnd(void* pBufferContext) override; + void OnLoopEnd(void* pBufferContext) override; + void OnVoiceError(void* pBufferContext, HRESULT Error) override; + +private: + GpAudioChannelXAudio2 *m_owner; +}; diff --git a/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.cpp new file mode 100644 index 0000000..1ef3713 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.cpp @@ -0,0 +1,13 @@ +#include "GpAudioDriverFactoryXAudio2.h" +#include "GpAudioDriverXAudio2.h" + +IGpAudioDriver *GpAudioDriverFactoryXAudio2::Create(const GpAudioDriverProperties &properties) +{ + return GpAudioDriverXAudio2::Create(properties); +} + + +extern "C" __declspec(dllexport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties) +{ + return GpAudioDriverFactoryXAudio2::Create(properties); +} diff --git a/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.h b/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.h new file mode 100644 index 0000000..06ff9e9 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriverFactoryXAudio2.h @@ -0,0 +1,10 @@ +#pragma once + +class IGpAudioDriver; +struct GpAudioDriverProperties; + +class GpAudioDriverFactoryXAudio2 +{ +public: + static IGpAudioDriver *Create(const GpAudioDriverProperties &properties); +}; diff --git a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp new file mode 100644 index 0000000..cc15366 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.cpp @@ -0,0 +1,84 @@ +#include "GpAudioDriverXAudio2.h" + +#include "GpAudioChannelXAudio2.h" + +#include + +void GpAudioDriverXAudio2::Shutdown() +{ + delete this; +} + +const GpAudioDriverProperties &GpAudioDriverXAudio2::GetProperties() const +{ + return m_properties; +} + +IXAudio2 *GpAudioDriverXAudio2::GetXA2() const +{ + return m_xa2; +} + +IXAudio2MasteringVoice *GpAudioDriverXAudio2::GetMasteringVoice() const +{ + return m_mv; +} + +unsigned int GpAudioDriverXAudio2::GetRealSampleRate() const +{ + return m_realSampleRate; +} + +GpAudioDriverXAudio2 *GpAudioDriverXAudio2::Create(const GpAudioDriverProperties &properties) +{ + IXAudio2 *xa = nullptr; + IXAudio2MasteringVoice *mv = nullptr; + + const unsigned int realSampleRate = (properties.m_sampleRate + 50) / XAUDIO2_QUANTUM_DENOMINATOR * XAUDIO2_QUANTUM_DENOMINATOR; + + if (CoInitializeEx(nullptr, COINIT_MULTITHREADED) != S_OK) + { + CoUninitialize(); + return nullptr; + } + + UINT flags = 0; + if (properties.m_debug) + flags |= XAUDIO2_DEBUG_ENGINE; + + if (FAILED(XAudio2Create(&xa, flags, XAUDIO2_DEFAULT_PROCESSOR))) + { + CoUninitialize(); + return nullptr; + } + + if (FAILED(xa->CreateMasteringVoice(&mv, 2, realSampleRate, 0, nullptr, nullptr, AudioCategory_GameEffects))) + { + CoUninitialize(); + xa->Release(); + return nullptr; + } + + return new GpAudioDriverXAudio2(properties, realSampleRate, xa, mv); +} + +IGpAudioChannel *GpAudioDriverXAudio2::CreateChannel() +{ + return GpAudioChannelXAudio2::Create(this); +} + +GpAudioDriverXAudio2::GpAudioDriverXAudio2(const GpAudioDriverProperties &properties, unsigned int realSampleRate, IXAudio2* xa2, IXAudio2MasteringVoice *mv) + : m_properties(properties) + , m_realSampleRate(realSampleRate) + , m_xa2(xa2) + , m_mv(mv) +{ +} + +GpAudioDriverXAudio2::~GpAudioDriverXAudio2() +{ + m_mv->DestroyVoice(); + m_xa2->Release(); + + CoUninitialize(); +} diff --git a/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h new file mode 100644 index 0000000..227e41e --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriverXAudio2.h @@ -0,0 +1,32 @@ +#pragma once + +#include "IGpAudioDriver.h" +#include "GpCoreDefs.h" +#include "GpAudioDriverProperties.h" + +struct IXAudio2; +struct IXAudio2MasteringVoice; + +class GpAudioDriverXAudio2 : public IGpAudioDriver +{ +public: + IGpAudioChannel *CreateChannel() override; + void Shutdown() override; + + const GpAudioDriverProperties &GetProperties() const; + IXAudio2 *GetXA2() const; + IXAudio2MasteringVoice *GetMasteringVoice() const; + unsigned int GetRealSampleRate() const; + + static GpAudioDriverXAudio2 *Create(const GpAudioDriverProperties &properties); + +private: + GpAudioDriverXAudio2(const GpAudioDriverProperties &properties, unsigned int realSampleRate, IXAudio2* xa2, IXAudio2MasteringVoice *mv); + ~GpAudioDriverXAudio2(); + + GpAudioDriverProperties m_properties; + + IXAudio2* m_xa2; + IXAudio2MasteringVoice *m_mv; + unsigned int m_realSampleRate; +}; diff --git a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj new file mode 100644 index 0000000..72988d8 --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj @@ -0,0 +1,136 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {E3BDC783-8646-433E-ADF0-8B6390D36669} + GpAudioDriverXAudio2 + 10.0.17763.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + DynamicLibrary + true + v141 + MultiByte + + + DynamicLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters new file mode 100644 index 0000000..9df99fe --- /dev/null +++ b/GpAudioDriver_XAudio2/GpAudioDriver_XAudio2.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/GpCommon.props b/GpCommon.props new file mode 100644 index 0000000..60b9d2f --- /dev/null +++ b/GpCommon.props @@ -0,0 +1,10 @@ + + + + + + $(SolutionDir)GpCommon;$(IncludePath) + + + + \ No newline at end of file diff --git a/GpCommon/EGpAudioDriverType.h b/GpCommon/EGpAudioDriverType.h new file mode 100644 index 0000000..5351735 --- /dev/null +++ b/GpCommon/EGpAudioDriverType.h @@ -0,0 +1,8 @@ +#pragma once + +enum EGpAudioDriverType +{ + EGpAudioDriverType_XAudio2, + + EGpAudioDriverType_Count, +}; diff --git a/GpD3D/EGpDisplayDriverType.h b/GpCommon/EGpDisplayDriverType.h similarity index 100% rename from GpD3D/EGpDisplayDriverType.h rename to GpCommon/EGpDisplayDriverType.h diff --git a/GpCommon/GpAudioDriverProperties.h b/GpCommon/GpAudioDriverProperties.h new file mode 100644 index 0000000..0dbefe5 --- /dev/null +++ b/GpCommon/GpAudioDriverProperties.h @@ -0,0 +1,13 @@ +#pragma once + +#include "EGpAudioDriverType.h" + +class IGpAudioDriver; + +struct GpAudioDriverProperties +{ + EGpAudioDriverType m_type; + + unsigned int m_sampleRate; + bool m_debug; +}; diff --git a/GpD3D/GpCoreDefs.h b/GpCommon/GpCoreDefs.h similarity index 100% rename from GpD3D/GpCoreDefs.h rename to GpCommon/GpCoreDefs.h diff --git a/GpCommon/GpDisplayDriverProperties.h b/GpCommon/GpDisplayDriverProperties.h new file mode 100644 index 0000000..5afa346 --- /dev/null +++ b/GpCommon/GpDisplayDriverProperties.h @@ -0,0 +1,26 @@ +#pragma once + +#include "EGpDisplayDriverType.h" + +class IGpDisplayDriver; +class GpFiber; + +struct GpDisplayDriverProperties +{ + typedef void(*TickFunc_t)(void *context, GpFiber *vosFiber); + + EGpDisplayDriverType m_type; + + unsigned int m_frameTimeLockNumerator; + unsigned int m_frameTimeLockDenominator; + + unsigned int m_frameTimeLockMinNumerator; + unsigned int m_frameTimeLockMinDenominator; + + unsigned int m_frameTimeLockMaxNumerator; + unsigned int m_frameTimeLockMaxDenominator; + + // Tick function and context to call when a frame needs to be served. + TickFunc_t m_tickFunc; + void *m_tickFuncContext; +}; diff --git a/GpD3D/GpWindows.h b/GpCommon/GpWindows.h similarity index 100% rename from GpD3D/GpWindows.h rename to GpCommon/GpWindows.h diff --git a/GpCommon/IGpAudioChannel.h b/GpCommon/IGpAudioChannel.h new file mode 100644 index 0000000..ab1dddd --- /dev/null +++ b/GpCommon/IGpAudioChannel.h @@ -0,0 +1,10 @@ +#pragma once + +struct IGpAudioChannelCallbacks; + +struct IGpAudioChannel +{ + virtual void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) = 0; + virtual void PostBuffer(const void *buffer, size_t bufferSize) = 0; + virtual void Destroy() = 0; +}; diff --git a/GpCommon/IGpAudioChannelCallbacks.h b/GpCommon/IGpAudioChannelCallbacks.h new file mode 100644 index 0000000..75a8801 --- /dev/null +++ b/GpCommon/IGpAudioChannelCallbacks.h @@ -0,0 +1,6 @@ +#pragma once + +struct IGpAudioChannelCallbacks +{ + virtual void NotifyBufferFinished() = 0; +}; diff --git a/GpCommon/IGpAudioDriver.h b/GpCommon/IGpAudioDriver.h new file mode 100644 index 0000000..2a33d8d --- /dev/null +++ b/GpCommon/IGpAudioDriver.h @@ -0,0 +1,13 @@ +#pragma once + +struct IGpAudioChannel; + +class IGpAudioDriver +{ +public: + virtual ~IGpAudioDriver() {} + + virtual IGpAudioChannel *CreateChannel() = 0; + + virtual void Shutdown() = 0; +}; diff --git a/GpD3D/IGpDisplayDriver.h b/GpCommon/IGpDisplayDriver.h similarity index 58% rename from GpD3D/IGpDisplayDriver.h rename to GpCommon/IGpDisplayDriver.h index a2231d7..8520e1b 100644 --- a/GpD3D/IGpDisplayDriver.h +++ b/GpCommon/IGpDisplayDriver.h @@ -1,4 +1,6 @@ -#pragma once +#pragma once + +#include "PixelFormat.h" // Display drivers are responsible for timing and calling the game tick function. class IGpDisplayDriver @@ -9,5 +11,5 @@ public: virtual void Run() = 0; virtual void Shutdown() = 0; - virtual void GetDisplayResolution(unsigned int &width, unsigned int &height) = 0; + virtual void GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) = 0; }; diff --git a/GpD3D/GpAppEnvironment.cpp b/GpD3D/GpAppEnvironment.cpp index 34310b8..bb5c9d4 100644 --- a/GpD3D/GpAppEnvironment.cpp +++ b/GpD3D/GpAppEnvironment.cpp @@ -1,130 +1,139 @@ -#include "GpAppEnvironment.h" -#include "GpFiberStarter.h" -#include "GpAppInterface.h" -#include "GpPLGlueDisplayDriver.h" -#include "GpFiber.h" -#include "HostSuspendCallArgument.h" - -#include - -GpAppEnvironment::GpAppEnvironment() - : m_applicationState(ApplicationState_NotStarted) - , m_displayDriver(nullptr) - , m_applicationFiber(nullptr) - , m_vosFiber(nullptr) - , m_suspendCallID(PortabilityLayer::HostSuspendCallID_Unknown) - , m_suspendArgs(nullptr) - , m_suspendReturnValue(nullptr) -{ -} - -GpAppEnvironment::~GpAppEnvironment() -{ - assert(m_applicationFiber == nullptr); -} - -void GpAppEnvironment::Init() -{ -} - -void GpAppEnvironment::Tick(GpFiber *vosFiber) -{ - GpAppInterface_Get()->PL_IncrementTickCounter(1); - - m_vosFiber = vosFiber; - - if (m_applicationState == ApplicationState_WaitingForEvents) - m_applicationState = ApplicationState_Running; - - for (;;) - { - switch (m_applicationState) - { - case ApplicationState_NotStarted: - InitializeApplicationState(); - m_applicationFiber = GpFiberStarter::StartFiber(GpAppEnvironment::StaticAppThreadFunc, this, vosFiber); - m_applicationState = ApplicationState_Running; - break; - case ApplicationState_WaitingForEvents: - return; - case ApplicationState_Running: - m_applicationFiber->YieldTo(); - break; - case ApplicationState_SystemCall: - { - PortabilityLayer::HostSuspendCallID callID = m_suspendCallID; - const PortabilityLayer::HostSuspendCallArgument *args = m_suspendArgs; - PortabilityLayer::HostSuspendCallArgument *returnValue = m_suspendReturnValue; - - DispatchSystemCall(callID, args, returnValue); - assert(m_applicationState != ApplicationState_SystemCall); - } - break; - case ApplicationState_TimedSuspend: - if (m_delaySuspendTicks <= 1) - m_applicationState = ApplicationState_Running; - else - { - m_delaySuspendTicks--; - return; - } - break; - default: - assert(false); - break; - }; - } -} - -void GpAppEnvironment::SetDisplayDriver(IGpDisplayDriver *displayDriver) -{ - m_displayDriver = displayDriver; -} - -void GpAppEnvironment::StaticAppThreadFunc(void *context) -{ - static_cast(context)->AppThreadFunc(); -} - -void GpAppEnvironment::AppThreadFunc() -{ - GpAppInterface_Get()->ApplicationMain(); -} - -void GpAppEnvironment::InitializeApplicationState() -{ - GpAppInterface_Get()->PL_HostDisplayDriver_SetInstance(GpPLGlueDisplayDriver::GetInstance()); - GpAppInterface_Get()->PL_InstallHostSuspendHook(GpAppEnvironment::StaticSuspendHookFunc, this); - - SynchronizeState(); -} - -void GpAppEnvironment::SynchronizeState() -{ - GpPLGlueDisplayDriver::GetInstance()->SetGpDisplayDriver(m_displayDriver); -} - -void GpAppEnvironment::StaticSuspendHookFunc(void *context, PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) -{ - GpAppEnvironment *appEnv = static_cast(context); - - appEnv->m_suspendCallID = callID; - appEnv->m_suspendArgs = args; - appEnv->m_suspendReturnValue = returnValue; - appEnv->m_applicationState = ApplicationState_SystemCall; - - appEnv->m_vosFiber->YieldTo(); -} - -void GpAppEnvironment::DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) -{ - switch (callID) - { - case PortabilityLayer::HostSuspendCallID_Delay: - m_applicationState = ApplicationState_TimedSuspend; - m_delaySuspendTicks = args[0].m_uint; - break; - default: - assert(false); - } -} +#include "GpAppEnvironment.h" +#include "GpFiberStarter.h" +#include "GpAppInterface.h" +#include "GpPLGlueAudioDriver.h" +#include "GpPLGlueDisplayDriver.h" +#include "GpFiber.h" +#include "HostSuspendCallArgument.h" + +#include + +GpAppEnvironment::GpAppEnvironment() + : m_applicationState(ApplicationState_NotStarted) + , m_displayDriver(nullptr) + , m_audioDriver(nullptr) + , m_applicationFiber(nullptr) + , m_vosFiber(nullptr) + , m_suspendCallID(PortabilityLayer::HostSuspendCallID_Unknown) + , m_suspendArgs(nullptr) + , m_suspendReturnValue(nullptr) +{ +} + +GpAppEnvironment::~GpAppEnvironment() +{ + assert(m_applicationFiber == nullptr); +} + +void GpAppEnvironment::Init() +{ +} + +void GpAppEnvironment::Tick(GpFiber *vosFiber) +{ + GpAppInterface_Get()->PL_IncrementTickCounter(1); + + m_vosFiber = vosFiber; + + if (m_applicationState == ApplicationState_WaitingForEvents) + m_applicationState = ApplicationState_Running; + + for (;;) + { + switch (m_applicationState) + { + case ApplicationState_NotStarted: + InitializeApplicationState(); + m_applicationFiber = GpFiberStarter::StartFiber(GpAppEnvironment::StaticAppThreadFunc, this, vosFiber); + m_applicationState = ApplicationState_Running; + break; + case ApplicationState_WaitingForEvents: + return; + case ApplicationState_Running: + m_applicationFiber->YieldTo(); + break; + case ApplicationState_SystemCall: + { + PortabilityLayer::HostSuspendCallID callID = m_suspendCallID; + const PortabilityLayer::HostSuspendCallArgument *args = m_suspendArgs; + PortabilityLayer::HostSuspendCallArgument *returnValue = m_suspendReturnValue; + + DispatchSystemCall(callID, args, returnValue); + assert(m_applicationState != ApplicationState_SystemCall); + } + break; + case ApplicationState_TimedSuspend: + if (m_delaySuspendTicks <= 1) + m_applicationState = ApplicationState_Running; + else + { + m_delaySuspendTicks--; + return; + } + break; + default: + assert(false); + break; + }; + } +} + +void GpAppEnvironment::SetDisplayDriver(IGpDisplayDriver *displayDriver) +{ + m_displayDriver = displayDriver; +} + +void GpAppEnvironment::SetAudioDriver(IGpAudioDriver *audioDriver) +{ + m_audioDriver = audioDriver; +} + +void GpAppEnvironment::StaticAppThreadFunc(void *context) +{ + static_cast(context)->AppThreadFunc(); +} + +void GpAppEnvironment::AppThreadFunc() +{ + GpAppInterface_Get()->ApplicationMain(); +} + +void GpAppEnvironment::InitializeApplicationState() +{ + GpAppInterface_Get()->PL_HostDisplayDriver_SetInstance(GpPLGlueDisplayDriver::GetInstance()); + GpAppInterface_Get()->PL_HostAudioDriver_SetInstance(GpPLGlueAudioDriver::GetInstance()); + GpAppInterface_Get()->PL_InstallHostSuspendHook(GpAppEnvironment::StaticSuspendHookFunc, this); + + SynchronizeState(); +} + +void GpAppEnvironment::SynchronizeState() +{ + GpPLGlueDisplayDriver::GetInstance()->SetGpDisplayDriver(m_displayDriver); + GpPLGlueAudioDriver::GetInstance()->SetGpAudioDriver(m_audioDriver); +} + +void GpAppEnvironment::StaticSuspendHookFunc(void *context, PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) +{ + GpAppEnvironment *appEnv = static_cast(context); + + appEnv->m_suspendCallID = callID; + appEnv->m_suspendArgs = args; + appEnv->m_suspendReturnValue = returnValue; + appEnv->m_applicationState = ApplicationState_SystemCall; + + appEnv->m_vosFiber->YieldTo(); +} + +void GpAppEnvironment::DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue) +{ + switch (callID) + { + case PortabilityLayer::HostSuspendCallID_Delay: + m_applicationState = ApplicationState_TimedSuspend; + m_delaySuspendTicks = args[0].m_uint; + break; + default: + assert(false); + } +} diff --git a/GpD3D/GpAppEnvironment.h b/GpD3D/GpAppEnvironment.h index 443122a..77ad884 100644 --- a/GpD3D/GpAppEnvironment.h +++ b/GpD3D/GpAppEnvironment.h @@ -1,55 +1,58 @@ -#pragma once - -#include "HostSuspendCallID.h" - -#include - -namespace PortabilityLayer -{ - union HostSuspendCallArgument; -} - -class IGpDisplayDriver; -class GpFiber; - -class GpAppEnvironment -{ -public: - GpAppEnvironment(); - ~GpAppEnvironment(); - - void Init(); - - void Tick(GpFiber *vosFiber); - void SetDisplayDriver(IGpDisplayDriver *displayDriver); - -private: - enum ApplicationState - { - ApplicationState_NotStarted, - ApplicationState_WaitingForEvents, - ApplicationState_Running, - ApplicationState_Terminated, - ApplicationState_SystemCall, - ApplicationState_TimedSuspend, - }; - - static void StaticAppThreadFunc(void *context); - void AppThreadFunc(); - void InitializeApplicationState(); - void SynchronizeState(); - - static void StaticSuspendHookFunc(void *context, PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue); - void DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue); - - ApplicationState m_applicationState; - IGpDisplayDriver *m_displayDriver; - GpFiber *m_applicationFiber; - GpFiber *m_vosFiber; - - uint32_t m_delaySuspendTicks; - - PortabilityLayer::HostSuspendCallID m_suspendCallID; - const PortabilityLayer::HostSuspendCallArgument *m_suspendArgs; - PortabilityLayer::HostSuspendCallArgument *m_suspendReturnValue; -}; +#pragma once + +#include "HostSuspendCallID.h" + +#include + +namespace PortabilityLayer +{ + union HostSuspendCallArgument; +} + +class IGpDisplayDriver; +class IGpAudioDriver; +class GpFiber; + +class GpAppEnvironment +{ +public: + GpAppEnvironment(); + ~GpAppEnvironment(); + + void Init(); + + void Tick(GpFiber *vosFiber); + void SetDisplayDriver(IGpDisplayDriver *displayDriver); + void SetAudioDriver(IGpAudioDriver *audioDriver); + +private: + enum ApplicationState + { + ApplicationState_NotStarted, + ApplicationState_WaitingForEvents, + ApplicationState_Running, + ApplicationState_Terminated, + ApplicationState_SystemCall, + ApplicationState_TimedSuspend, + }; + + static void StaticAppThreadFunc(void *context); + void AppThreadFunc(); + void InitializeApplicationState(); + void SynchronizeState(); + + static void StaticSuspendHookFunc(void *context, PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue); + void DispatchSystemCall(PortabilityLayer::HostSuspendCallID callID, const PortabilityLayer::HostSuspendCallArgument *args, PortabilityLayer::HostSuspendCallArgument *returnValue); + + ApplicationState m_applicationState; + IGpDisplayDriver *m_displayDriver; + IGpAudioDriver *m_audioDriver; + GpFiber *m_applicationFiber; + GpFiber *m_vosFiber; + + uint32_t m_delaySuspendTicks; + + PortabilityLayer::HostSuspendCallID m_suspendCallID; + const PortabilityLayer::HostSuspendCallArgument *m_suspendArgs; + PortabilityLayer::HostSuspendCallArgument *m_suspendReturnValue; +}; diff --git a/GpD3D/GpAudioDriverFactory.cpp b/GpD3D/GpAudioDriverFactory.cpp new file mode 100644 index 0000000..9c84e70 --- /dev/null +++ b/GpD3D/GpAudioDriverFactory.cpp @@ -0,0 +1,23 @@ +#include "GpAudioDriverFactory.h" +#include "GpAudioDriverProperties.h" + +#include + +IGpAudioDriver *GpAudioDriverFactory::CreateAudioDriver(const GpAudioDriverProperties &properties) +{ + assert(properties.m_type < EGpAudioDriverType_Count); + + if (ms_registry[properties.m_type]) + return ms_registry[properties.m_type](properties); + else + return nullptr; +} + +void GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType type, FactoryFunc_t func) +{ + assert(type < EGpAudioDriverType_Count); + + ms_registry[type] = func; +} + +GpAudioDriverFactory::FactoryFunc_t GpAudioDriverFactory::ms_registry[EGpAudioDriverType_Count]; diff --git a/GpD3D/GpAudioDriverFactory.h b/GpD3D/GpAudioDriverFactory.h new file mode 100644 index 0000000..e090bb7 --- /dev/null +++ b/GpD3D/GpAudioDriverFactory.h @@ -0,0 +1,18 @@ +#pragma once + +#include "EGpAudioDriverType.h" + +class IGpAudioDriver; +struct GpAudioDriverProperties; + +class GpAudioDriverFactory +{ +public: + typedef IGpAudioDriver *(*FactoryFunc_t)(const GpAudioDriverProperties &properties); + + static IGpAudioDriver *CreateAudioDriver(const GpAudioDriverProperties &properties); + static void RegisterAudioDriverFactory(EGpAudioDriverType type, FactoryFunc_t func); + +private: + static FactoryFunc_t ms_registry[EGpAudioDriverType_Count]; +}; diff --git a/GpD3D/GpAudioDriverXAudio2.h b/GpD3D/GpAudioDriverXAudio2.h new file mode 100644 index 0000000..e69de29 diff --git a/GpD3D/GpD3D.vcxproj b/GpD3D/GpD3D.vcxproj index 24013d1..02272b8 100644 --- a/GpD3D/GpD3D.vcxproj +++ b/GpD3D/GpD3D.vcxproj @@ -60,21 +60,29 @@ + + + + + + + + @@ -128,9 +136,9 @@ + - @@ -139,19 +147,27 @@ + + + + + + + + + - @@ -159,17 +175,26 @@ + + + + + + {6233c3f2-5781-488e-b190-4fa8836f5a77} + + {e3bdc783-8646-433e-adf0-8b6390d36669} + diff --git a/GpD3D/GpD3D.vcxproj.filters b/GpD3D/GpD3D.vcxproj.filters index 54e5914..65ff133 100644 --- a/GpD3D/GpD3D.vcxproj.filters +++ b/GpD3D/GpD3D.vcxproj.filters @@ -36,9 +36,6 @@ Source Files - - Source Files - Source Files @@ -60,6 +57,21 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + @@ -98,9 +110,6 @@ Header Files - - Header Files - Header Files @@ -125,5 +134,38 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + \ No newline at end of file diff --git a/GpD3D/GpDisplayDriverD3D11.cpp b/GpD3D/GpDisplayDriverD3D11.cpp index 53fb13d..417cc92 100644 --- a/GpD3D/GpDisplayDriverD3D11.cpp +++ b/GpD3D/GpDisplayDriverD3D11.cpp @@ -1,290 +1,294 @@ -#include "GpDisplayDriverD3D11.h" -#include "GpWindows.h" -#include "GpFiber_Win32.h" - -#include -#include - -#include - -#pragma comment (lib, "d3d11.lib") - -void DebugPrintf(const char *fmt, ...) -{ - char buf[256]; - va_list argp; - va_start(argp, fmt); - vsnprintf_s(buf, 255, fmt, argp); - OutputDebugString(buf); - va_end(argp); -} - -LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) -{ - switch (message) - { - case WM_DESTROY: - { - PostQuitMessage(0); - return 0; - } - break; - } - - return DefWindowProc(hWnd, message, wParam, lParam); -} - -void StartD3DForWindow(HWND hWnd, IDXGISwapChain1*& swapChain) -{ - DXGI_SWAP_CHAIN_DESC1 swapChainDesc; - - ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); - - swapChainDesc.BufferCount = 2; - swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - swapChainDesc.SampleDesc.Count = 1; - swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; - - DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFullscreenDesc; - - ZeroMemory(&swapChainFullscreenDesc, sizeof(swapChainFullscreenDesc)); - - swapChainFullscreenDesc.Windowed = TRUE; - swapChainFullscreenDesc.RefreshRate.Numerator = 60; - swapChainFullscreenDesc.RefreshRate.Denominator = 1; - - UINT flags = 0; - const D3D_FEATURE_LEVEL featureLevels[] = - { - D3D_FEATURE_LEVEL_9_1 - }; - - flags |= D3D11_CREATE_DEVICE_DEBUG; - - ID3D11Device *device = NULL; - ID3D11DeviceContext *context = NULL; - - D3D_FEATURE_LEVEL selectedFeatureLevel; - - HRESULT result = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, sizeof(featureLevels) / sizeof(featureLevels[0]), - D3D11_SDK_VERSION, &device, &selectedFeatureLevel, &context); - - IDXGIDevice2 *dxgiDevice = nullptr; - result = device->QueryInterface(__uuidof(IDXGIDevice2), reinterpret_cast(&dxgiDevice)); - - IDXGIAdapter *dxgiAdapter = nullptr; - result = dxgiDevice->GetAdapter(&dxgiAdapter); - - IDXGIFactory2 *dxgiFactory = nullptr; - result = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast(&dxgiFactory)); - - result = dxgiFactory->CreateSwapChainForHwnd(device, hWnd, &swapChainDesc, nullptr, nullptr, &swapChain); -} - -bool GpDisplayDriverD3D11::PresentFrameAndSync() -{ - DXGI_PRESENT_PARAMETERS presentParams; - - ZeroMemory(&presentParams, sizeof(presentParams)); - - UINT lastPresentCount = 0; - - if (FAILED(m_SwapChain->GetLastPresentCount(&lastPresentCount))) - return false; - - if (FAILED(m_SwapChain->Present1(1, 0, &presentParams))) - return false; - - //DebugPrintf("r: %i\n", static_cast(r)); - - DXGI_FRAME_STATISTICS stats; - if (FAILED(m_SwapChain->GetFrameStatistics(&stats))) - return false; - - if (stats.SyncQPCTime.QuadPart != 0) - { - if (m_SyncTimeBase.QuadPart == 0) - m_SyncTimeBase = stats.SyncQPCTime; - - LARGE_INTEGER timestamp; - timestamp.QuadPart = stats.SyncQPCTime.QuadPart - m_SyncTimeBase.QuadPart; - - bool compacted = false; - if (m_PresentHistory.Size() > 0) - { - CompactedPresentHistoryItem &lastItem = m_PresentHistory[m_PresentHistory.Size() - 1]; - LONGLONG timeDelta = timestamp.QuadPart - lastItem.m_Timestamp.QuadPart; - - if (timeDelta < 0) - timeDelta = 0; // This should never happen - - if (timeDelta * static_cast(m_Properties.m_FrameTimeLockDenominator) < m_QPFrequency.QuadPart * static_cast(m_Properties.m_FrameTimeLockNumerator)) - { - lastItem.m_NumFrames++; - compacted = true; - } - } - - if (!compacted) - { - if (m_PresentHistory.Size() == m_PresentHistory.CAPACITY) - m_PresentHistory.RemoveFromStart(); - - CompactedPresentHistoryItem *newItem = m_PresentHistory.Append(); - newItem->m_Timestamp = timestamp; - newItem->m_NumFrames = 1; - } - } - - if (m_PresentHistory.Size() >= 2) - { - const size_t presentHistorySizeMinusOne = m_PresentHistory.Size() - 1; - unsigned int numFrames = 0; - for (size_t i = 0; i < presentHistorySizeMinusOne; i++) - numFrames += m_PresentHistory[i].m_NumFrames; - - LONGLONG timeFrame = m_PresentHistory[presentHistorySizeMinusOne].m_Timestamp.QuadPart - m_PresentHistory[0].m_Timestamp.QuadPart; - - unsigned int cancelledFrames = 0; - LONGLONG cancelledTime = 0; - - const int overshootTolerance = 2; - - for (size_t i = 0; i < presentHistorySizeMinusOne; i++) - { - LONGLONG blockTimeframe = m_PresentHistory[i + 1].m_Timestamp.QuadPart - m_PresentHistory[i].m_Timestamp.QuadPart; - unsigned int blockNumFrames = m_PresentHistory[i].m_NumFrames; - - if (blockTimeframe * static_cast(numFrames) >= timeFrame * static_cast(blockNumFrames) * overshootTolerance) - { - cancelledTime += blockTimeframe; - cancelledFrames += blockNumFrames; - } - } - - numFrames -= cancelledFrames; - timeFrame -= cancelledTime; - - // timeFrame / numFrames = Frame timestep - // Unless Frame timestep is within the frame lock range, a.k.a. - // timeFrame / numFrames / qpFreq >= minFrameTimeNum / minFrameTimeDenom - - bool isInFrameTimeLock = false; - if (timeFrame * static_cast(m_Properties.m_FrameTimeLockMinDenominator) >= static_cast(numFrames) * static_cast(m_Properties.m_FrameTimeLockMinNumerator) * m_QPFrequency.QuadPart - && timeFrame * static_cast(m_Properties.m_FrameTimeLockMaxDenominator) <= static_cast(numFrames) * static_cast(m_Properties.m_FrameTimeLockMaxNumerator) * m_QPFrequency.QuadPart) - { - isInFrameTimeLock = true; - } - - LONGLONG frameTimeStep = m_FrameTimeSliceSize; - if (!isInFrameTimeLock) - { - const int MAX_FRAMES_PER_STEP = 4; - - frameTimeStep = timeFrame / numFrames; - if (frameTimeStep > m_FrameTimeSliceSize * MAX_FRAMES_PER_STEP) - frameTimeStep = m_FrameTimeSliceSize * MAX_FRAMES_PER_STEP; - } - - m_FrameTimeAccumulated += frameTimeStep; - while (m_FrameTimeAccumulated >= m_FrameTimeSliceSize) - { - m_Properties.m_TickFunc(m_Properties.m_TickFuncContext, m_vosFiber); - m_FrameTimeAccumulated -= m_FrameTimeSliceSize; - } - } - - return true; -} - -void GpDisplayDriverD3D11::Run() -{ - HWND hWnd; - WNDCLASSEX wc; - - LPVOID fiber = ConvertThreadToFiberEx(this, 0); - if (!fiber) - return; // ??? - - m_vosFiber = new GpFiber_Win32(fiber); - - ZeroMemory(&wc, sizeof(wc)); - - wc.cbSize = sizeof(WNDCLASSEX); - wc.style = CS_HREDRAW | CS_VREDRAW; - wc.lpfnWndProc = WinProc; - wc.hInstance = g_gpWindowsGlobals.m_hInstance; - wc.hCursor = LoadCursor(NULL, IDC_ARROW); - wc.hbrBackground = (HBRUSH)COLOR_WINDOW; - wc.lpszClassName = "GPD3D11WindowClass"; - - RegisterClassEx(&wc); - - LONG windowStyle = WS_OVERLAPPEDWINDOW; - HMENU menus = NULL; - - // TODO: Fix the resolution here - RECT wr = { 0, 0, m_windowWidth, m_windowHeight }; - AdjustWindowRect(&wr, windowStyle, menus != NULL); - - hWnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", L"GlidePort", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, g_gpWindowsGlobals.m_hInstance, NULL); - - ShowWindow(hWnd, g_gpWindowsGlobals.m_nCmdShow); - - StartD3DForWindow(hWnd, m_SwapChain); - - LARGE_INTEGER lastTimestamp; - memset(&lastTimestamp, 0, sizeof(lastTimestamp)); - - MSG msg; - for (;;) - { - if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - - DispatchMessage(&msg); - - if (msg.message == WM_QUIT) - break; - } - else - { - PresentFrameAndSync(); - } - } - - // Exit - ConvertFiberToThread(); -} - -void GpDisplayDriverD3D11::Shutdown() -{ - delete this; -} - -void GpDisplayDriverD3D11::GetDisplayResolution(unsigned int &width, unsigned int &height) -{ - width = m_windowWidth; - height = m_windowHeight; -} - -GpDisplayDriverD3D11 *GpDisplayDriverD3D11::Create(const GpDisplayDriverProperties &properties) -{ - return new GpDisplayDriverD3D11(properties); -} - -GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties) - : m_Properties(properties) - , m_FrameTimeAccumulated(0) - , m_windowWidth(640) - , m_windowHeight(480) - , m_vosFiber(nullptr) -{ - memset(&m_SyncTimeBase, 0, sizeof(m_SyncTimeBase)); - - QueryPerformanceFrequency(&m_QPFrequency); - - m_FrameTimeSliceSize = m_QPFrequency.QuadPart * static_cast(properties.m_FrameTimeLockNumerator) / static_cast(properties.m_FrameTimeLockDenominator); -} +#include "GpDisplayDriverD3D11.h" +#include "GpWindows.h" +#include "GpFiber_Win32.h" + +#include +#include + +#include + +#pragma comment (lib, "d3d11.lib") + +void DebugPrintf(const char *fmt, ...) +{ + char buf[256]; + va_list argp; + va_start(argp, fmt); + vsnprintf_s(buf, 255, fmt, argp); + OutputDebugString(buf); + va_end(argp); +} + +LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_DESTROY: + { + PostQuitMessage(0); + return 0; + } + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +void StartD3DForWindow(HWND hWnd, IDXGISwapChain1*& swapChain) +{ + DXGI_SWAP_CHAIN_DESC1 swapChainDesc; + + ZeroMemory(&swapChainDesc, sizeof(swapChainDesc)); + + swapChainDesc.BufferCount = 2; + swapChainDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + swapChainDesc.SampleDesc.Count = 1; + swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; + + DXGI_SWAP_CHAIN_FULLSCREEN_DESC swapChainFullscreenDesc; + + ZeroMemory(&swapChainFullscreenDesc, sizeof(swapChainFullscreenDesc)); + + swapChainFullscreenDesc.Windowed = TRUE; + swapChainFullscreenDesc.RefreshRate.Numerator = 60; + swapChainFullscreenDesc.RefreshRate.Denominator = 1; + + UINT flags = 0; + const D3D_FEATURE_LEVEL featureLevels[] = + { + D3D_FEATURE_LEVEL_9_1 + }; + + flags |= D3D11_CREATE_DEVICE_DEBUG; + + ID3D11Device *device = NULL; + ID3D11DeviceContext *context = NULL; + + D3D_FEATURE_LEVEL selectedFeatureLevel; + + HRESULT result = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, NULL, flags, featureLevels, sizeof(featureLevels) / sizeof(featureLevels[0]), + D3D11_SDK_VERSION, &device, &selectedFeatureLevel, &context); + + IDXGIDevice2 *dxgiDevice = nullptr; + result = device->QueryInterface(__uuidof(IDXGIDevice2), reinterpret_cast(&dxgiDevice)); + + IDXGIAdapter *dxgiAdapter = nullptr; + result = dxgiDevice->GetAdapter(&dxgiAdapter); + + IDXGIFactory2 *dxgiFactory = nullptr; + result = dxgiAdapter->GetParent(__uuidof(IDXGIFactory2), reinterpret_cast(&dxgiFactory)); + + result = dxgiFactory->CreateSwapChainForHwnd(device, hWnd, &swapChainDesc, nullptr, nullptr, &swapChain); +} + +bool GpDisplayDriverD3D11::PresentFrameAndSync() +{ + DXGI_PRESENT_PARAMETERS presentParams; + + ZeroMemory(&presentParams, sizeof(presentParams)); + + UINT lastPresentCount = 0; + + if (FAILED(m_swapChain->GetLastPresentCount(&lastPresentCount))) + return false; + + if (FAILED(m_swapChain->Present1(1, 0, &presentParams))) + return false; + + //DebugPrintf("r: %i\n", static_cast(r)); + + DXGI_FRAME_STATISTICS stats; + if (FAILED(m_swapChain->GetFrameStatistics(&stats))) + return false; + + if (stats.SyncQPCTime.QuadPart != 0) + { + if (m_syncTimeBase.QuadPart == 0) + m_syncTimeBase = stats.SyncQPCTime; + + LARGE_INTEGER timestamp; + timestamp.QuadPart = stats.SyncQPCTime.QuadPart - m_syncTimeBase.QuadPart; + + bool compacted = false; + if (m_presentHistory.Size() > 0) + { + CompactedPresentHistoryItem &lastItem = m_presentHistory[m_presentHistory.Size() - 1]; + LONGLONG timeDelta = timestamp.QuadPart - lastItem.m_timestamp.QuadPart; + + if (timeDelta < 0) + timeDelta = 0; // This should never happen + + if (timeDelta * static_cast(m_properties.m_frameTimeLockDenominator) < m_QPFrequency.QuadPart * static_cast(m_properties.m_frameTimeLockNumerator)) + { + lastItem.m_numFrames++; + compacted = true; + } + } + + if (!compacted) + { + if (m_presentHistory.Size() == m_presentHistory.CAPACITY) + m_presentHistory.RemoveFromStart(); + + CompactedPresentHistoryItem *newItem = m_presentHistory.Append(); + newItem->m_timestamp = timestamp; + newItem->m_numFrames = 1; + } + } + + if (m_presentHistory.Size() >= 2) + { + const size_t presentHistorySizeMinusOne = m_presentHistory.Size() - 1; + unsigned int numFrames = 0; + for (size_t i = 0; i < presentHistorySizeMinusOne; i++) + numFrames += m_presentHistory[i].m_numFrames; + + LONGLONG timeFrame = m_presentHistory[presentHistorySizeMinusOne].m_timestamp.QuadPart - m_presentHistory[0].m_timestamp.QuadPart; + + unsigned int cancelledFrames = 0; + LONGLONG cancelledTime = 0; + + const int overshootTolerance = 2; + + for (size_t i = 0; i < presentHistorySizeMinusOne; i++) + { + LONGLONG blockTimeframe = m_presentHistory[i + 1].m_timestamp.QuadPart - m_presentHistory[i].m_timestamp.QuadPart; + unsigned int blockNumFrames = m_presentHistory[i].m_numFrames; + + if (blockTimeframe * static_cast(numFrames) >= timeFrame * static_cast(blockNumFrames) * overshootTolerance) + { + cancelledTime += blockTimeframe; + cancelledFrames += blockNumFrames; + } + } + + numFrames -= cancelledFrames; + timeFrame -= cancelledTime; + + // timeFrame / numFrames = Frame timestep + // Unless Frame timestep is within the frame lock range, a.k.a. + // timeFrame / numFrames / qpFreq >= minFrameTimeNum / minFrameTimeDenom + + bool isInFrameTimeLock = false; + if (timeFrame * static_cast(m_properties.m_frameTimeLockMinDenominator) >= static_cast(numFrames) * static_cast(m_properties.m_frameTimeLockMinNumerator) * m_QPFrequency.QuadPart + && timeFrame * static_cast(m_properties.m_frameTimeLockMaxDenominator) <= static_cast(numFrames) * static_cast(m_properties.m_frameTimeLockMaxNumerator) * m_QPFrequency.QuadPart) + { + isInFrameTimeLock = true; + } + + LONGLONG frameTimeStep = m_frameTimeSliceSize; + if (!isInFrameTimeLock) + { + const int MAX_FRAMES_PER_STEP = 4; + + frameTimeStep = timeFrame / numFrames; + if (frameTimeStep > m_frameTimeSliceSize * MAX_FRAMES_PER_STEP) + frameTimeStep = m_frameTimeSliceSize * MAX_FRAMES_PER_STEP; + } + + m_frameTimeAccumulated += frameTimeStep; + while (m_frameTimeAccumulated >= m_frameTimeSliceSize) + { + m_properties.m_tickFunc(m_properties.m_tickFuncContext, m_vosFiber); + m_frameTimeAccumulated -= m_frameTimeSliceSize; + } + } + + return true; +} + +void GpDisplayDriverD3D11::Run() +{ + HWND hWnd; + WNDCLASSEX wc; + + LPVOID fiber = ConvertThreadToFiberEx(this, 0); + if (!fiber) + return; // ??? + + m_vosFiber = new GpFiber_Win32(fiber); + + ZeroMemory(&wc, sizeof(wc)); + + wc.cbSize = sizeof(WNDCLASSEX); + wc.style = CS_HREDRAW | CS_VREDRAW; + wc.lpfnWndProc = WinProc; + wc.hInstance = g_gpWindowsGlobals.m_hInstance; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)COLOR_WINDOW; + wc.lpszClassName = "GPD3D11WindowClass"; + + RegisterClassEx(&wc); + + LONG windowStyle = WS_OVERLAPPEDWINDOW; + HMENU menus = NULL; + + // TODO: Fix the resolution here + RECT wr = { 0, 0, m_windowWidth, m_windowHeight }; + AdjustWindowRect(&wr, windowStyle, menus != NULL); + + hWnd = CreateWindowExW(NULL, L"GPD3D11WindowClass", L"GlidePort", WS_OVERLAPPEDWINDOW, 300, 300, wr.right - wr.left, wr.bottom - wr.top, NULL, menus, g_gpWindowsGlobals.m_hInstance, NULL); + + ShowWindow(hWnd, g_gpWindowsGlobals.m_nCmdShow); + + StartD3DForWindow(hWnd, m_swapChain); + + LARGE_INTEGER lastTimestamp; + memset(&lastTimestamp, 0, sizeof(lastTimestamp)); + + MSG msg; + for (;;) + { + if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + + DispatchMessage(&msg); + + if (msg.message == WM_QUIT) + break; + } + else + { + PresentFrameAndSync(); + } + } + + // Exit + ConvertFiberToThread(); +} + +void GpDisplayDriverD3D11::Shutdown() +{ + delete this; +} + +void GpDisplayDriverD3D11::GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *pixelFormat) +{ + if (width) + *width = m_windowWidth; + if (height) + *height = m_windowHeight; + if (pixelFormat) + *pixelFormat = PortabilityLayer::PixelFormat_8BitStandard; +} + +GpDisplayDriverD3D11 *GpDisplayDriverD3D11::Create(const GpDisplayDriverProperties &properties) +{ + return new GpDisplayDriverD3D11(properties); +} + +GpDisplayDriverD3D11::GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties) + : m_properties(properties) + , m_frameTimeAccumulated(0) + , m_windowWidth(640) + , m_windowHeight(480) + , m_vosFiber(nullptr) +{ + memset(&m_syncTimeBase, 0, sizeof(m_syncTimeBase)); + + QueryPerformanceFrequency(&m_QPFrequency); + + m_frameTimeSliceSize = m_QPFrequency.QuadPart * static_cast(properties.m_frameTimeLockNumerator) / static_cast(properties.m_frameTimeLockDenominator); +} diff --git a/GpD3D/GpDisplayDriverD3D11.h b/GpD3D/GpDisplayDriverD3D11.h index 5e47bc8..8f4af90 100644 --- a/GpD3D/GpDisplayDriverD3D11.h +++ b/GpD3D/GpDisplayDriverD3D11.h @@ -1,50 +1,52 @@ -#pragma once - -#include "GpWindows.h" -#include "GpRingBuffer.h" - -#include "IGpDisplayDriver.h" -#include "GpCoreDefs.h" -#include "GpDisplayDriverProperties.h" - -struct IDXGISwapChain1; - -class GpDisplayDriverD3D11 : public IGpDisplayDriver -{ -public: - void Run() override; - void Shutdown() override; - - void GetDisplayResolution(unsigned int &width, unsigned int &height) override; - - static GpDisplayDriverD3D11 *Create(const GpDisplayDriverProperties &properties); - -private: - GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties); - - bool PresentFrameAndSync(); - - IDXGISwapChain1 *m_SwapChain; - - struct CompactedPresentHistoryItem - { - LARGE_INTEGER m_Timestamp; - unsigned int m_NumFrames; - }; - - GpRingBuffer m_PresentHistory; - GpDisplayDriverProperties m_Properties; - - LARGE_INTEGER m_SyncTimeBase; - LARGE_INTEGER m_QPFrequency; - UINT m_ExpectedSyncDelta; - bool m_IsResettingSwapChain; - - LONGLONG m_FrameTimeAccumulated; - LONGLONG m_FrameTimeSliceSize; - - DWORD m_windowWidth; - DWORD m_windowHeight; - - GpFiber *m_vosFiber; -}; +#pragma once + +#include "GpWindows.h" +#include "GpRingBuffer.h" + +#include "IGpDisplayDriver.h" +#include "GpCoreDefs.h" +#include "GpDisplayDriverProperties.h" + +#include "PixelFormat.h" + +struct IDXGISwapChain1; + +class GpDisplayDriverD3D11 : public IGpDisplayDriver +{ +public: + void Run() override; + void Shutdown() override; + + void GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) override; + + static GpDisplayDriverD3D11 *Create(const GpDisplayDriverProperties &properties); + +private: + GpDisplayDriverD3D11(const GpDisplayDriverProperties &properties); + + bool PresentFrameAndSync(); + + IDXGISwapChain1 *m_swapChain; + + struct CompactedPresentHistoryItem + { + LARGE_INTEGER m_timestamp; + unsigned int m_numFrames; + }; + + GpRingBuffer m_presentHistory; + GpDisplayDriverProperties m_properties; + + LARGE_INTEGER m_syncTimeBase; + LARGE_INTEGER m_QPFrequency; + UINT m_expectedSyncDelta; + bool m_isResettingSwapChain; + + LONGLONG m_frameTimeAccumulated; + LONGLONG m_frameTimeSliceSize; + + DWORD m_windowWidth; + DWORD m_windowHeight; + + GpFiber *m_vosFiber; +}; diff --git a/GpD3D/GpDisplayDriverFactory.cpp b/GpD3D/GpDisplayDriverFactory.cpp index fc79bb1..f196225 100644 --- a/GpD3D/GpDisplayDriverFactory.cpp +++ b/GpD3D/GpDisplayDriverFactory.cpp @@ -5,10 +5,10 @@ IGpDisplayDriver *GpDisplayDriverFactory::CreateDisplayDriver(const GpDisplayDriverProperties &properties) { - assert(properties.m_Type < EGpDisplayDriverType_Count); + assert(properties.m_type < EGpDisplayDriverType_Count); - if (ms_Registry[properties.m_Type]) - return ms_Registry[properties.m_Type](properties); + if (ms_Registry[properties.m_type]) + return ms_Registry[properties.m_type](properties); else return nullptr; } diff --git a/GpD3D/GpDisplayDriverProperties.h b/GpD3D/GpDisplayDriverProperties.h deleted file mode 100644 index 79ae5bd..0000000 --- a/GpD3D/GpDisplayDriverProperties.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include "EGpDisplayDriverType.h" - -class IGpDisplayDriver; -class GpFiber; - -struct GpDisplayDriverProperties -{ - typedef void(*TickFunc_t)(void *context, GpFiber *vosFiber); - - EGpDisplayDriverType m_Type; - - unsigned int m_FrameTimeLockNumerator; - unsigned int m_FrameTimeLockDenominator; - - unsigned int m_FrameTimeLockMinNumerator; - unsigned int m_FrameTimeLockMinDenominator; - - unsigned int m_FrameTimeLockMaxNumerator; - unsigned int m_FrameTimeLockMaxDenominator; - - // Tick function and context to call when a frame needs to be served. - TickFunc_t m_TickFunc; - void *m_TickFuncContext; -}; diff --git a/GpD3D/GpEvent.h b/GpD3D/GpEvent.h deleted file mode 100644 index 7ccaac1..0000000 --- a/GpD3D/GpEvent.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include "GpCoreDefs.h" - -class GpEvent final -{ -public: - void Wait(); - void WaitMSec(unsigned int msec); - void Signal(); - void Reset(); - void Destroy(); - - static GpEvent *Create(bool autoReset, bool startSignalled); - -private: - explicit GpEvent(void *privateData); - ~GpEvent(); - - void *m_PrivateData; -}; diff --git a/GpD3D/GpEvent_Win32.cpp b/GpD3D/GpEvent_Win32.cpp deleted file mode 100644 index 6323697..0000000 --- a/GpD3D/GpEvent_Win32.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "GpEvent.h" -#include "GpWindows.h" - -#include - -GpEvent::~GpEvent() -{ - CloseHandle(static_cast(m_PrivateData)); -} - -void GpEvent::Wait() -{ - WaitForSingleObject(static_cast(m_PrivateData), INFINITE); -} - -void GpEvent::WaitMSec(unsigned int msec) -{ - assert(msec < MAXDWORD); - WaitForSingleObject(static_cast(m_PrivateData), static_cast(msec)); -} - -void GpEvent::Signal() -{ - SetEvent(static_cast(m_PrivateData)); -} - -void GpEvent::Reset() -{ - ResetEvent(static_cast(m_PrivateData)); -} - -void GpEvent::Destroy() -{ - delete this; -} - -GpEvent *GpEvent::Create(bool autoReset, bool startSignalled) -{ - HANDLE handle = CreateEventA(nullptr, autoReset ? FALSE : TRUE, startSignalled ? TRUE : FALSE, nullptr); - if (!handle) - return nullptr; - - return new GpEvent(handle); -} - -GpEvent::GpEvent(void *privateData) - : m_PrivateData(privateData) -{ -} diff --git a/GpD3D/GpMain.cpp b/GpD3D/GpMain.cpp index 25ee433..a9a87d6 100644 --- a/GpD3D/GpMain.cpp +++ b/GpD3D/GpMain.cpp @@ -1,48 +1,64 @@ -#include "GpMain.h" -#include "GpDisplayDriverFactory.h" -#include "GpDisplayDriverProperties.h" -#include "GpGlobalConfig.h" -#include "GpAppEnvironment.h" -#include "IGpDisplayDriver.h" - -#include - -namespace -{ - void TickAppEnvironment(void *context, GpFiber *vosFiber) - { - static_cast(context)->Tick(vosFiber); - } -} - -int GpMain::Run() -{ - GpAppEnvironment *appEnvironment = new GpAppEnvironment(); - - GpDisplayDriverProperties ddProps; - memset(&ddProps, 0, sizeof(ddProps)); - - ddProps.m_FrameTimeLockNumerator = 1; - ddProps.m_FrameTimeLockDenominator = 60; - - // +/- 1% tolerance for frame time variance - ddProps.m_FrameTimeLockMinNumerator = 99; - ddProps.m_FrameTimeLockMinDenominator = 6000; - ddProps.m_FrameTimeLockMaxNumerator = 101; - ddProps.m_FrameTimeLockMaxDenominator = 6000; - - ddProps.m_TickFunc = TickAppEnvironment; - ddProps.m_TickFuncContext = appEnvironment; - ddProps.m_Type = g_gpGlobalConfig.m_displayDriverType; - - IGpDisplayDriver *displayDriver = GpDisplayDriverFactory::CreateDisplayDriver(ddProps); - - appEnvironment->Init(); - - appEnvironment->SetDisplayDriver(displayDriver); - - // Start the display loop - displayDriver->Run(); - - return 0; -} +#include "GpMain.h" +#include "GpAudioDriverFactory.h" +#include "GpAudioDriverProperties.h" +#include "GpDisplayDriverFactory.h" +#include "GpDisplayDriverProperties.h" +#include "GpGlobalConfig.h" +#include "GpAppEnvironment.h" +#include "IGpAudioDriver.h" +#include "IGpDisplayDriver.h" + +#include + +namespace +{ + void TickAppEnvironment(void *context, GpFiber *vosFiber) + { + static_cast(context)->Tick(vosFiber); + } +} + +int GpMain::Run() +{ + GpAppEnvironment *appEnvironment = new GpAppEnvironment(); + + GpDisplayDriverProperties ddProps; + memset(&ddProps, 0, sizeof(ddProps)); + + ddProps.m_type = EGpDisplayDriverType_D3D11; + + ddProps.m_frameTimeLockNumerator = 1; + ddProps.m_frameTimeLockDenominator = 60; + + // +/- 1% tolerance for frame time variance + ddProps.m_frameTimeLockMinNumerator = 99; + ddProps.m_frameTimeLockMinDenominator = 6000; + ddProps.m_frameTimeLockMaxNumerator = 101; + ddProps.m_frameTimeLockMaxDenominator = 6000; + + ddProps.m_tickFunc = TickAppEnvironment; + ddProps.m_tickFuncContext = appEnvironment; + ddProps.m_type = g_gpGlobalConfig.m_displayDriverType; + + GpAudioDriverProperties adProps; + memset(&adProps, 0, sizeof(adProps)); + + // The sample rate used in all of Glider PRO's sound is 0x56ee8ba3 + // This appears to be the "standard" Mac sample rate, probably rounded from 244800/11. + adProps.m_type = EGpAudioDriverType_XAudio2; + adProps.m_sampleRate = (244800 * 2 + 11) / (11 * 2); + adProps.m_debug = true; + + IGpDisplayDriver *displayDriver = GpDisplayDriverFactory::CreateDisplayDriver(ddProps); + IGpAudioDriver *audioDriver = GpAudioDriverFactory::CreateAudioDriver(adProps); + + appEnvironment->Init(); + + appEnvironment->SetDisplayDriver(displayDriver); + appEnvironment->SetAudioDriver(audioDriver); + + // Start the display loop + displayDriver->Run(); + + return 0; +} diff --git a/GpD3D/GpMain_Win32.cpp b/GpD3D/GpMain_Win32.cpp index f55a8bd..37eaa90 100644 --- a/GpD3D/GpMain_Win32.cpp +++ b/GpD3D/GpMain_Win32.cpp @@ -1,33 +1,36 @@ -#include "GpWindows.h" -#include "GpMain.h" -#include "GpDisplayDriverFactory.h" -#include "GpDisplayDriverFactoryD3D11.h" -#include "GpGlobalConfig.h" -#include "GpFileSystem_Win32.h" -#include "GpAppInterface.h" -#include "GpSystemServices_Win32.h" - -#include "HostFileSystem.h" - -#include - -#include - -GPWindowsGlobals g_gpWindowsGlobals; - -int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) -{ - GpAppInterface_Get()->PL_HostFileSystem_SetInstance(GpFileSystem_Win32::GetInstance()); - GpAppInterface_Get()->PL_HostSystemServices_SetInstance(GpSystemServices_Win32::GetInstance()); - - g_gpWindowsGlobals.m_hInstance = hInstance; - g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance; - g_gpWindowsGlobals.m_cmdLine = lpCmdLine; - g_gpWindowsGlobals.m_nCmdShow = nCmdShow; - - g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_D3D11; - - GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDisplayDriverFactoryD3D11::Create); - - return GpMain::Run(); -} +#include "GpMain.h" +#include "GpAudioDriverFactory.h" +#include "GpDisplayDriverFactory.h" +#include "GpDisplayDriverFactoryD3D11.h" +#include "GpGlobalConfig.h" +#include "GpFileSystem_Win32.h" +#include "GpAppInterface.h" +#include "GpSystemServices_Win32.h" + +#include "HostFileSystem.h" + +#include "GpWindows.h" + +#include + +GPWindowsGlobals g_gpWindowsGlobals; + +extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties); + +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) +{ + GpAppInterface_Get()->PL_HostFileSystem_SetInstance(GpFileSystem_Win32::GetInstance()); + GpAppInterface_Get()->PL_HostSystemServices_SetInstance(GpSystemServices_Win32::GetInstance()); + + g_gpWindowsGlobals.m_hInstance = hInstance; + g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance; + g_gpWindowsGlobals.m_cmdLine = lpCmdLine; + g_gpWindowsGlobals.m_nCmdShow = nCmdShow; + + g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_D3D11; + + GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDisplayDriverFactoryD3D11::Create); + GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2); + + return GpMain::Run(); +} diff --git a/GpD3D/GpMutex_Win32.cpp b/GpD3D/GpMutex_Win32.cpp new file mode 100644 index 0000000..849fc4f --- /dev/null +++ b/GpD3D/GpMutex_Win32.cpp @@ -0,0 +1,42 @@ +#include "GpMutex_Win32.h" + +#include "GpWindows.h" + +#include +#include + +void GpMutex_Win32::Destroy() +{ + this->~GpMutex_Win32(); + free(this); +} + +void GpMutex_Win32::Lock() +{ + EnterCriticalSection(&m_critSection); +} + +void GpMutex_Win32::Unlock() +{ + LeaveCriticalSection(&m_critSection); +} + + +GpMutex_Win32 *GpMutex_Win32::Create() +{ + void *storage = malloc(sizeof(GpMutex_Win32)); + if (!storage) + return nullptr; + + return new (storage) GpMutex_Win32(); +} + +GpMutex_Win32::GpMutex_Win32() +{ + InitializeCriticalSection(&m_critSection); +} + +GpMutex_Win32::~GpMutex_Win32() +{ + DeleteCriticalSection(&m_critSection); +} diff --git a/GpD3D/GpMutex_Win32.h b/GpD3D/GpMutex_Win32.h new file mode 100644 index 0000000..c31734b --- /dev/null +++ b/GpD3D/GpMutex_Win32.h @@ -0,0 +1,21 @@ +#pragma once + +#include "HostMutex.h" + +#include "GpWindows.h" + +class GpMutex_Win32 final : public PortabilityLayer::HostMutex +{ +public: + void Destroy() override; + void Lock() override; + void Unlock() override; + + static GpMutex_Win32 *Create(); + +private: + const GpMutex_Win32(); + ~GpMutex_Win32(); + + CRITICAL_SECTION m_critSection; +}; diff --git a/GpD3D/GpPLGlueAudioChannel.cpp b/GpD3D/GpPLGlueAudioChannel.cpp new file mode 100644 index 0000000..1926e9c --- /dev/null +++ b/GpD3D/GpPLGlueAudioChannel.cpp @@ -0,0 +1,49 @@ +#include "GpPLGlueAudioChannel.h" +#include "ClientAudioChannelContext.h" +#include "IGpAudioChannel.h" + +#include +#include + +void GpPLGlueAudioChannel::SetClientAudioChannelContext(PortabilityLayer::ClientAudioChannelContext *context) +{ + m_clientContext = context; + m_audioChannel->SetAudioChannelContext(this); +} + +void GpPLGlueAudioChannel::PostBuffer(const void *buffer, size_t bufferSize) +{ + m_audioChannel->PostBuffer(buffer, bufferSize); +} + +void GpPLGlueAudioChannel::Destroy() +{ + this->~GpPLGlueAudioChannel(); + free(this); +} + +void GpPLGlueAudioChannel::NotifyBufferFinished() +{ + if (m_clientContext) + m_clientContext->NotifyBufferFinished(); +} + +GpPLGlueAudioChannel *GpPLGlueAudioChannel::Create(IGpAudioChannel *audioChannel) +{ + void *storage = malloc(sizeof(GpPLGlueAudioChannel)); + if (!storage) + return nullptr; + + return new (storage) GpPLGlueAudioChannel(audioChannel); +} + +GpPLGlueAudioChannel::GpPLGlueAudioChannel(IGpAudioChannel *audioChannel) + : m_audioChannel(audioChannel) + , m_clientContext(nullptr) +{ +} + +GpPLGlueAudioChannel::~GpPLGlueAudioChannel() +{ + m_audioChannel->Destroy(); +} diff --git a/GpD3D/GpPLGlueAudioChannel.h b/GpD3D/GpPLGlueAudioChannel.h new file mode 100644 index 0000000..c0cbba4 --- /dev/null +++ b/GpD3D/GpPLGlueAudioChannel.h @@ -0,0 +1,25 @@ +#pragma once + +#include "HostAudioChannel.h" +#include "IGpAudioChannelCallbacks.h" + +struct IGpAudioChannel; + +class GpPLGlueAudioChannel final : public PortabilityLayer::HostAudioChannel, public IGpAudioChannelCallbacks +{ +public: + void SetClientAudioChannelContext(PortabilityLayer::ClientAudioChannelContext *context) override; + void PostBuffer(const void *buffer, size_t bufferSize) override; + void Destroy() override; + + void NotifyBufferFinished() override; + + static GpPLGlueAudioChannel *Create(IGpAudioChannel *audioChannel); + +private: + explicit GpPLGlueAudioChannel(IGpAudioChannel *audioChannel); + ~GpPLGlueAudioChannel(); + + PortabilityLayer::ClientAudioChannelContext *m_clientContext; + IGpAudioChannel *m_audioChannel; +}; diff --git a/GpD3D/GpPLGlueAudioDriver.cpp b/GpD3D/GpPLGlueAudioDriver.cpp new file mode 100644 index 0000000..f85ccc2 --- /dev/null +++ b/GpD3D/GpPLGlueAudioDriver.cpp @@ -0,0 +1,38 @@ +#include "GpPLGlueAudioDriver.h" + +#include "GpPLGlueAudioChannel.h" +#include "IGpAudioChannel.h" +#include "IGpAudioDriver.h" + +GpPLGlueAudioDriver::GpPLGlueAudioDriver() + : m_audioDriver(nullptr) +{ +} + +PortabilityLayer::HostAudioChannel *GpPLGlueAudioDriver::CreateChannel() +{ + IGpAudioChannel *channel = m_audioDriver->CreateChannel(); + if (!channel) + return nullptr; + + PortabilityLayer::HostAudioChannel *glueChannel = GpPLGlueAudioChannel::Create(channel); + if (!glueChannel) + { + channel->Destroy(); + return nullptr; + } + + return glueChannel; +} + +GpPLGlueAudioDriver *GpPLGlueAudioDriver::GetInstance() +{ + return &ms_instance; +} + +void GpPLGlueAudioDriver::SetGpAudioDriver(IGpAudioDriver *audioDriver) +{ + m_audioDriver = audioDriver; +} + +GpPLGlueAudioDriver GpPLGlueAudioDriver::ms_instance; diff --git a/GpD3D/GpPLGlueAudioDriver.h b/GpD3D/GpPLGlueAudioDriver.h new file mode 100644 index 0000000..2006321 --- /dev/null +++ b/GpD3D/GpPLGlueAudioDriver.h @@ -0,0 +1,22 @@ +#pragma once + +#include "HostAudioDriver.h" + +class IGpAudioDriver; + +class GpPLGlueAudioDriver final : public PortabilityLayer::HostAudioDriver +{ +public: + GpPLGlueAudioDriver(); + + PortabilityLayer::HostAudioChannel *CreateChannel() override; + + void SetGpAudioDriver(IGpAudioDriver *audioDriver); + + static GpPLGlueAudioDriver *GetInstance(); + +private: + IGpAudioDriver *m_audioDriver; + + static GpPLGlueAudioDriver ms_instance; +}; diff --git a/GpD3D/GpPLGlueDisplayDriver.cpp b/GpD3D/GpPLGlueDisplayDriver.cpp index c7ab62d..3ac92a4 100644 --- a/GpD3D/GpPLGlueDisplayDriver.cpp +++ b/GpD3D/GpPLGlueDisplayDriver.cpp @@ -6,9 +6,9 @@ GpPLGlueDisplayDriver::GpPLGlueDisplayDriver() { } -void GpPLGlueDisplayDriver::GetDisplayResolution(unsigned int &width, unsigned int &height) +void GpPLGlueDisplayDriver::GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) { - m_displayDriver->GetDisplayResolution(width, height); + m_displayDriver->GetDisplayResolution(width, height, bpp); } void GpPLGlueDisplayDriver::HideCursor() diff --git a/GpD3D/GpPLGlueDisplayDriver.h b/GpD3D/GpPLGlueDisplayDriver.h index 6ded7c4..6f0a653 100644 --- a/GpD3D/GpPLGlueDisplayDriver.h +++ b/GpD3D/GpPLGlueDisplayDriver.h @@ -9,7 +9,7 @@ class GpPLGlueDisplayDriver final : public PortabilityLayer::HostDisplayDriver public: GpPLGlueDisplayDriver(); - void GetDisplayResolution(unsigned int &width, unsigned int &height) override; + void GetDisplayResolution(unsigned int *width, unsigned int *height, PortabilityLayer::PixelFormat *bpp) override; void HideCursor() override; void SetGpDisplayDriver(IGpDisplayDriver *displayDriver); diff --git a/GpD3D/GpSystemServices_Win32.cpp b/GpD3D/GpSystemServices_Win32.cpp index 42c6328..a2e4153 100644 --- a/GpD3D/GpSystemServices_Win32.cpp +++ b/GpD3D/GpSystemServices_Win32.cpp @@ -1,19 +1,40 @@ -#include "GpSystemServices_Win32.h" +#include "GpSystemServices_Win32.h" +#include "GpMutex_Win32.h" +#include "GpThreadEvent_Win32.h" + +#include + +#pragma push_macro("CreateMutex") +#ifdef CreateMutex +#undef CreateMutex +#endif + +GpSystemServices_Win32::GpSystemServices_Win32() +{ +} + +uint32_t GpSystemServices_Win32::GetTime() const +{ + // PL_NotYetImplemented + return 0; +} + +PortabilityLayer::HostMutex *GpSystemServices_Win32::CreateMutex() +{ + return GpMutex_Win32::Create(); +} -#include - -GpSystemServices_Win32::GpSystemServices_Win32() -{ -} - -uint32_t GpSystemServices_Win32::GetTime() const -{ - return 0; -} - -GpSystemServices_Win32 *GpSystemServices_Win32::GetInstance() -{ - return &ms_instance; -} - -GpSystemServices_Win32 GpSystemServices_Win32::ms_instance; +PortabilityLayer::HostThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled) +{ + return GpThreadEvent_Win32::Create(autoReset, startSignaled); +} + +GpSystemServices_Win32 *GpSystemServices_Win32::GetInstance() +{ + return &ms_instance; +} + +GpSystemServices_Win32 GpSystemServices_Win32::ms_instance; + + +#pragma pop_macro("CreateMutex") diff --git a/GpD3D/GpSystemServices_Win32.h b/GpD3D/GpSystemServices_Win32.h index 89b089e..0abbdac 100644 --- a/GpD3D/GpSystemServices_Win32.h +++ b/GpD3D/GpSystemServices_Win32.h @@ -1,19 +1,28 @@ -#pragma once - -#include "HostSystemServices.h" -#include "GpCoreDefs.h" -#include "GpWindows.h" - - -class GpSystemServices_Win32 final : public PortabilityLayer::HostSystemServices -{ -public: - GpSystemServices_Win32(); - - uint32_t GetTime() const override; - - static GpSystemServices_Win32 *GetInstance(); - -private: - static GpSystemServices_Win32 ms_instance; -}; +#pragma once + +#include "HostSystemServices.h" +#include "GpCoreDefs.h" +#include "GpWindows.h" + +#pragma push_macro("CreateMutex") +#ifdef CreateMutex +#undef CreateMutex +#endif + + +class GpSystemServices_Win32 final : public PortabilityLayer::HostSystemServices +{ +public: + GpSystemServices_Win32(); + + uint32_t GetTime() const override; + PortabilityLayer::HostMutex *CreateMutex() override; + PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override; + + static GpSystemServices_Win32 *GetInstance(); + +private: + static GpSystemServices_Win32 ms_instance; +}; + +#pragma pop_macro("CreateMutex") diff --git a/GpD3D/GpThreadEvent_Win32.cpp b/GpD3D/GpThreadEvent_Win32.cpp new file mode 100644 index 0000000..2e49ea7 --- /dev/null +++ b/GpD3D/GpThreadEvent_Win32.cpp @@ -0,0 +1,51 @@ +#include "GpThreadEvent_Win32.h" + +#include +#include + +void GpThreadEvent_Win32::Wait() +{ + WaitForSingleObject(m_event, INFINITE); +} + +void GpThreadEvent_Win32::WaitTimed(uint32_t msec) +{ + WaitForSingleObject(m_event, static_cast(msec)); +} + +void GpThreadEvent_Win32::Signal() +{ + SetEvent(m_event); +} + +void GpThreadEvent_Win32::Destroy() +{ + this->~GpThreadEvent_Win32(); + free(this); +} + +GpThreadEvent_Win32 *GpThreadEvent_Win32::Create(bool autoReset, bool startSignaled) +{ + HANDLE handle = CreateEventA(nullptr, autoReset ? FALSE : TRUE, startSignaled ? TRUE : FALSE, nullptr); + if (handle == nullptr) + return nullptr; + + void *storage = malloc(sizeof(GpThreadEvent_Win32)); + if (!storage) + { + CloseHandle(handle); + return nullptr; + } + + return new (storage) GpThreadEvent_Win32(handle); +} + +GpThreadEvent_Win32::GpThreadEvent_Win32(const HANDLE &handle) + : m_event(handle) +{ +} + +GpThreadEvent_Win32::~GpThreadEvent_Win32() +{ + CloseHandle(m_event); +} diff --git a/GpD3D/GpThreadEvent_Win32.h b/GpD3D/GpThreadEvent_Win32.h new file mode 100644 index 0000000..8977a9a --- /dev/null +++ b/GpD3D/GpThreadEvent_Win32.h @@ -0,0 +1,22 @@ +#pragma once + +#include "HostThreadEvent.h" + +#include "GpWindows.h" + +class GpThreadEvent_Win32 final : public PortabilityLayer::HostThreadEvent +{ +public: + void Wait() override; + void WaitTimed(uint32_t msec) override; + void Signal() override; + void Destroy() override; + + static GpThreadEvent_Win32 *Create(bool autoReset, bool startSignaled); + +private: + explicit GpThreadEvent_Win32(const HANDLE &handle); + ~GpThreadEvent_Win32(); + + HANDLE m_event; +}; diff --git a/GpMainApp.props b/GpMainApp.props new file mode 100644 index 0000000..cd7f92f --- /dev/null +++ b/GpMainApp.props @@ -0,0 +1,12 @@ + + + + + + + + GP_APP_DLL;%(PreprocessorDefinitions) + + + + \ No newline at end of file diff --git a/PictChecker/PictChecker.cpp b/PictChecker/PictChecker.cpp new file mode 100644 index 0000000..048f84c --- /dev/null +++ b/PictChecker/PictChecker.cpp @@ -0,0 +1,1318 @@ +#define _CRT_SECURE_NO_WARNINGS + +#include "MemReaderStream.h" +#include "MMHandleBlock.h" +#include "ResourceCompiledTypeList.h" +#include "ResourceFile.h" +#include "ResourceManager.h" +#include "CFileStream.h" +#include "QDPictDecoder.h" +#include "QDPictEmitContext.h" +#include "QDPictEmitScanlineParameters.h" +#include "QDPictHeader.h" +#include "QDPictOpcodes.h" +#include "RGBAColor.h" +#include "SharedTypes.h" +#include "stb_image_write.h" + +#include +#include +#include +#include + +using namespace PortabilityLayer; + +struct TempImage +{ + size_t m_width; + size_t m_height; + + Rect m_rect; + + RGBAColor *m_contents; +}; + +void UnpackBits8(std::vector &outVector, const std::vector &inData) +{ + size_t count = inData.size(); + size_t offset = 0; + while (offset < count) + { + int8_t headerByte; + memcpy(&headerByte, &inData[offset++], 1); + + if (headerByte >= 0) + { + const size_t litCount = headerByte + 1; + for (size_t i = 0; i < litCount; i++) + outVector.push_back(inData[offset++]); + } + else if (headerByte < 0 && headerByte != -128) + { + const size_t repCount = (1 - headerByte); + const uint8_t repeated = inData[offset++]; + for (size_t i = 0; i < repCount; i++) + outVector.push_back(repeated); + } + } +} + +void UnpackBits16(std::vector &outVector, const std::vector &inData) +{ + size_t count = inData.size(); + size_t offset = 0; + while (offset < count) + { + int8_t headerByte; + memcpy(&headerByte, &inData[offset++], 1); + + if (headerByte >= 0) + { + const size_t litCount = headerByte + 1; + for (size_t i = 0; i < litCount; i++) + { + const uint8_t highByte = inData[offset++]; + const uint8_t lowByte = inData[offset++]; + const uint16_t combined = (highByte << 8) | lowByte; + outVector.push_back(combined); + } + } + else if (headerByte < 0 && headerByte != -128) + { + const size_t repCount = (1 - headerByte); + + const uint8_t highByte = inData[offset++]; + const uint8_t lowByte = inData[offset++]; + const uint16_t combined = (highByte << 8) | lowByte; + + for (size_t i = 0; i < repCount; i++) + outVector.push_back(combined); + } + } +} + +void BlitLineRGB8(const std::vector &data, size_t first, RGBAColor *outScanline, size_t actualSize) +{ + for (size_t i = 0; i < actualSize; i++) + { + RGBAColor &color = outScanline[i]; + color.r = data[first + 0 * actualSize + i]; + color.g = data[first + 1 * actualSize + i]; + color.b = data[first + 2 * actualSize + i]; + color.a = 255; + } +} + +uint8_t FiveToEight(uint8_t v) +{ + return static_cast((v << 3) | (v >> 2)); +} + +void BlitLine16(const std::vector &data, size_t first, RGBAColor *outScanline, size_t actualSize) +{ + for (size_t i = 0; i < actualSize; i++) + { + RGBAColor &color = outScanline[i]; + const uint16_t packedInput = data[first + i]; + color.b = FiveToEight(packedInput & 0x1f); + color.g = FiveToEight((packedInput >> 5) & 0x1f); + color.r = FiveToEight((packedInput >> 10) & 0x1f); + + color.a = 255; + } +} + +void BlitLine1(const std::vector &data, size_t first, RGBAColor *outScanline, size_t actualSize) +{ + for (size_t i = 0; i < actualSize; i++) + { + RGBAColor &color = outScanline[i]; + + if ((data[first + i / 8] & (128 >> (i & 0x7))) == 0) + color.r = color.g = color.b = 255; + else + color.r = color.g = color.b = 0; + color.a = 255; + } +} + +void BlitLineIndexed(const std::vector &data, size_t first, RGBAColor *outScanline, size_t actualSize, int componentSize, const RGBAColor *clut) +{ + if (componentSize == 1) + { + for (size_t i = 0; i < actualSize; i++) + { + const int bitShift = 7 - (i & 7); + const int colorIndex = (data[first + i / 8] >> bitShift) & 0x1; + outScanline[i] = clut[colorIndex]; + } + } + else if (componentSize == 2) + { + for (size_t i = 0; i < actualSize; i++) + { + const int bitShift = 6 - (2 * (i & 3)); + const int colorIndex = (data[first + i / 4] >> bitShift) & 0x3; + outScanline[i] = clut[colorIndex]; + } + } + else if (componentSize == 4) + { + for (size_t i = 0; i < actualSize; i++) + { + const int bitShift = 4 - (4 * (i & 1)); + const int colorIndex = (data[first + i / 2] >> bitShift) & 0xf; + outScanline[i] = clut[colorIndex]; + } + } + else if (componentSize == 8) + { + for (size_t i = 0; i < actualSize; i++) + outScanline[i] = clut[data[first + i]]; + } + else + { + assert(false); + } +} + +void DecodeClutItemChannel(uint8_t &outChannel, const uint8_t *inChannel) +{ + outChannel = inChannel[0]; +} + +void DecodeClutItem(RGBAColor &decoded, const BEColorTableItem &clutItem) +{ + DecodeClutItemChannel(decoded.r, clutItem.m_red); + DecodeClutItemChannel(decoded.g, clutItem.m_green); + DecodeClutItemChannel(decoded.b, clutItem.m_blue); + decoded.a = 255; +} + +void AuditPackBitsRectOld(MemReaderStream &stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, TempImage &image) +{ + uint16_t rowSizeBytes = 0; + + bool isPixMap = false; + if (isDirect) + { + isPixMap = true; + // Skip base address (4) + unknown (2) + stream.SeekCurrent(4); + + stream.Read(&rowSizeBytes, sizeof(BEUInt16_t)); + ByteSwap::BigUInt16(rowSizeBytes); + + rowSizeBytes &= 0x7fff; + } + else + { + stream.Read(&rowSizeBytes, sizeof(BEUInt16_t)); + ByteSwap::BigUInt16(rowSizeBytes); + + if (pictVersion == 2) + { + if ((rowSizeBytes & 0x8000) != 0) + { + isPixMap = true; + rowSizeBytes &= 0x7fff; + } + else + printf("Actually a bitmap\n"); + } + } + + if (!isPixMap) + { + printf("Bitmap?\n"); + BEBitMap bitMap; + stream.Read(&bitMap, sizeof(BEBitMap)); + + BERect srcRectBE; + stream.Read(&srcRectBE, sizeof(BERect)); + + Rect srcRect = srcRectBE.ToRect(); + + BERect destRectBE; + stream.Read(&destRectBE, sizeof(BERect)); + + Rect destRect = destRectBE.ToRect(); + + BEUInt16_t transferMode; + stream.Read(&transferMode, 2); + + if (hasRegion) + { + BEUInt16_t regionSize; + stream.Read(®ionSize, 2); + + stream.SeekCurrent(regionSize - 2); + } + + assert(static_cast(destRect.right) <= image.m_width); + assert(static_cast(destRect.left) <= image.m_width); + assert(destRect.left < destRect.right); + + const int numCols = destRect.right - destRect.left; + const int firstCol = destRect.left; + const int firstRow = destRect.top; + + const int numLines = bitMap.m_bounds.bottom - bitMap.m_bounds.top; + if (isPackedFlag && rowSizeBytes >= 8) + { + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressed; + compressed.resize(lineByteCount); + + if (stream.Read(&compressed[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits8(decompressed, compressed); + + BlitLine1(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + else + { + std::vector decompressed; + decompressed.resize(rowSizeBytes); + + for (int i = 0; i < numLines; i++) + { + if (stream.Read(&decompressed[0], rowSizeBytes) != rowSizeBytes) + { + assert(false); + } + + BlitLine1(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + } + else + { + // Read RowBytes, if high bit is set, revert to bitmap + // If rowBytes < 8 or pack type == 1, data is unpacked + // If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images) + // If pack type == 3, 16-bit RLE + // If pack type == 4, 8-bit planar component RLE + + PL_STATIC_ASSERT(sizeof(BEPixMap) == 44); + + BEPixMap pixMap; + stream.Read(&pixMap, sizeof(BEPixMap)); + + printf("Pack type: %i\n", static_cast(pixMap.m_packType)); + + BEColorTableHeader clutHeader; + BEColorTableItem clutItems[256]; + + RGBAColor colors[256]; + for (int i = 0; i < 256; i++) + { + RGBAColor &color = colors[i]; + color.r = color.g = color.b = i; + color.a = 255; + } + + if (isDirect) + { + } + else + { + stream.Read(&clutHeader, sizeof(BEColorTableHeader)); + + assert(clutHeader.m_numItemsMinusOne <= 255); + + printf("CLUT items: %i\n", static_cast(clutHeader.m_numItemsMinusOne)); + uint16_t numColors = clutHeader.m_numItemsMinusOne + 1; + + stream.Read(clutItems, sizeof(BEColorTableItem) * numColors); + + for (int i = 0; i < numColors; i++) + DecodeClutItem(colors[i], clutItems[i]); + } + + BERect srcRectBE; + stream.Read(&srcRectBE, sizeof(BERect)); + + BERect destRectBE; + stream.Read(&destRectBE, sizeof(BERect)); + + const Rect srcRect = srcRectBE.ToRect(); + const Rect destRect = destRectBE.ToRect(); + + assert(static_cast(destRect.right) <= image.m_width); + assert(static_cast(destRect.left) <= image.m_width); + assert(destRect.left < destRect.right); + + const int numCols = destRect.right - destRect.left; + const int firstCol = destRect.left; + const int firstRow = destRect.top; + + BEUInt16_t transferMode; + stream.Read(&transferMode, 2); + + //assert(srcRect.left == pixMap.m_bounds.left && srcRect.top == pixMap.m_bounds.top); + //assert(srcRect.left == destRect.left && srcRect.top == destRect.top); + + + assert(isPackedFlag || rowSizeBytes < 8 || pixMap.m_packType == 1); + + if (hasRegion) + { + BEUInt16_t regionSize; + stream.Read(®ionSize, 2); + + stream.SeekCurrent(regionSize - 2); + } + + int packType = pixMap.m_packType; + if (isDirect && packType == 0) + { + switch (static_cast(pixMap.m_componentSize)) + { + case 16: + packType = 3; + break; + case 32: + packType = 4; + break; + default: + break; + } + } + + if (isDirect) + { + if (packType == 4) + { + assert(pixMap.m_componentCount == 3); + assert(pixMap.m_componentSize == 8); + } + else if (packType == 3) + { + assert(pixMap.m_componentCount == 3); + assert(pixMap.m_componentSize == 5); + } + else + { + assert(false); + } + } + + int numLines = pixMap.m_bounds.bottom - pixMap.m_bounds.top; + if (isDirect == false && (pixMap.m_packType == 1 || rowSizeBytes < 8)) + { + std::vector directLine; + directLine.resize(rowSizeBytes); + + for (int i = 0; i < numLines; i++) + { + if (stream.Read(&directLine[0], rowSizeBytes) != rowSizeBytes) + { + assert(false); + } + + if (pixMap.m_componentSize == 1) + { + BlitLine1(directLine, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + else if (pixMap.m_componentSize == 8) + { + BlitLineIndexed(directLine, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors); + } + else + { + assert(false); + } + } + } + else if (isDirect && packType == 4) + { + assert(pixMap.m_componentCount == 3); + assert(pixMap.m_componentSize == 8); + + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits8(decompressed, compressedLine); + + BlitLineRGB8(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + else if (isDirect && packType == 3) + { + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits16(decompressed, compressedLine); + + BlitLine16(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + else if (packType <= 1) + { + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits8(decompressed, compressedLine); + + BlitLineIndexed(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors); + } + } + else + { + assert(false); + } + + if ((stream.Tell() & 1) != 0) + stream.SeekCurrent(1); + } +} + + +void AuditPackBitsRect(MemReaderStream &stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, TempImage &image) +{ + if (false) + { + AuditPackBitsRectOld(stream, pictVersion, isPackedFlag, hasRegion, isDirect, image); + return; + } + + uint16_t rowSizeBytes = 0; + + bool isPixMap = false; + if (isDirect) + { + isPixMap = true; + // Skip base address + stream.SeekCurrent(4); + + stream.Read(&rowSizeBytes, sizeof(BEUInt16_t)); + ByteSwap::BigUInt16(rowSizeBytes); + + rowSizeBytes &= 0x7fff; + } + else + { + stream.Read(&rowSizeBytes, sizeof(BEUInt16_t)); + ByteSwap::BigUInt16(rowSizeBytes); + + if (pictVersion == 2) + { + if ((rowSizeBytes & 0x8000) != 0) + { + isPixMap = true; + rowSizeBytes &= 0x7fff; + } + else + printf("Actually a bitmap\n"); + } + } + + Rect srcRect; + Rect destRect; + uint16_t transferMode; + + BEColorTableHeader clutHeader; + BEColorTableItem clutItems[256]; + + RGBAColor colors[256]; + for (int i = 0; i < 256; i++) + { + RGBAColor &color = colors[i]; + color.r = color.g = color.b = i; + color.a = 255; + } + + BEPixMap pixMapBE; + + int packType = 0; + + if (!isPixMap) + { + BEBitMap bitMap; + stream.Read(&bitMap, sizeof(BEBitMap)); + + packType = (isPackedFlag && rowSizeBytes >= 8) ? 0 : 1; + + pixMapBE.m_bounds = bitMap.m_bounds; + pixMapBE.m_version = 2; + pixMapBE.m_packType = packType; + pixMapBE.m_hRes = 72 << 16; + pixMapBE.m_vRes = 72 << 16; + pixMapBE.m_vRes = 72 << 16; + pixMapBE.m_pixelType = 16; // FIXME: Use direct instead + pixMapBE.m_pixelSize = 1; + pixMapBE.m_componentCount = 1; + pixMapBE.m_componentSize = 1; + pixMapBE.m_planeSizeBytes = 0; + pixMapBE.m_clutHandle = 0; + pixMapBE.m_unused = 0; + + BERect srcRectBE; + stream.Read(&srcRectBE, sizeof(BERect)); + + BERect destRectBE; + stream.Read(&destRectBE, sizeof(BERect)); + + srcRect = srcRectBE.ToRect(); + destRect = destRectBE.ToRect(); + + stream.Read(&transferMode, 2); + ByteSwap::BigUInt16(transferMode); + + if (hasRegion) + { + BEUInt16_t regionSize; + stream.Read(®ionSize, 2); + + stream.SeekCurrent(regionSize - 2); + } + + colors[0].r = colors[0].g = colors[0].b = 255; + colors[0].a = 255; + + colors[1].r = colors[1].g = colors[1].b = 0; + colors[1].a = 255; + } + else + { + // If rowBytes < 8 or pack type == 1, data is unpacked + // If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images) + // If pack type == 3, 16-bit RLE + // If pack type == 4, 8-bit planar component RLE + + PL_STATIC_ASSERT(sizeof(BEPixMap) == 44); + + stream.Read(&pixMapBE, sizeof(BEPixMap)); + + if (isDirect) + { + } + else + { + stream.Read(&clutHeader, sizeof(BEColorTableHeader)); + + assert(clutHeader.m_numItemsMinusOne <= 255); + + printf("CLUT items: %i\n", static_cast(clutHeader.m_numItemsMinusOne)); + uint16_t numColors = clutHeader.m_numItemsMinusOne + 1; + + stream.Read(clutItems, sizeof(BEColorTableItem) * numColors); + + for (int i = 0; i < numColors; i++) + DecodeClutItem(colors[i], clutItems[i]); + } + + BERect srcRectBE; + stream.Read(&srcRectBE, sizeof(BERect)); + + BERect destRectBE; + stream.Read(&destRectBE, sizeof(BERect)); + + srcRect = srcRectBE.ToRect(); + destRect = destRectBE.ToRect(); + + assert(destRect.left < destRect.right); + assert(destRect.top < destRect.bottom); + + const int numCols = destRect.right - destRect.left; + const int firstCol = destRect.left; + const int firstRow = destRect.top; + + BEUInt16_t transferMode; + stream.Read(&transferMode, 2); + + //assert(srcRect.left == pixMap.m_bounds.left && srcRect.top == pixMap.m_bounds.top); + //assert(srcRect.left == destRect.left && srcRect.top == destRect.top); + + assert(srcRect.left == pixMapBE.m_bounds.left && srcRect.top == pixMapBE.m_bounds.top); + + assert(isPackedFlag || rowSizeBytes < 8 || pixMapBE.m_packType == 1); + + if (hasRegion) + { + BEUInt16_t regionSize; + stream.Read(®ionSize, 2); + + stream.SeekCurrent(regionSize - 2); + } + + packType = pixMapBE.m_packType; + if (isDirect && packType == 0) + { + switch (static_cast(pixMapBE.m_componentSize)) + { + case 16: + packType = 3; + break; + case 32: + packType = 4; + break; + default: + break; + } + } + + if (isDirect) + { + if (packType == 4) + { + assert(pixMapBE.m_componentCount == 3); + assert(pixMapBE.m_componentSize == 8); + } + else if (packType == 3) + { + assert(pixMapBE.m_componentCount == 3); + assert(pixMapBE.m_componentSize == 5); + } + else + { + assert(false); + } + } + } + + + assert(srcRect.right - srcRect.left == destRect.right - destRect.left); + assert(srcRect.bottom - srcRect.top == destRect.bottom - destRect.top); + + assert(srcRect.right - srcRect.left <= pixMapBE.m_bounds.right - pixMapBE.m_bounds.left); + assert(srcRect.bottom - srcRect.top <= pixMapBE.m_bounds.bottom - pixMapBE.m_bounds.top); + + bool noBlit = false; + assert(destRect.left <= destRect.right); + assert(destRect.top <= destRect.bottom); + if (destRect.right <= image.m_rect.left) + noBlit = true; + if (destRect.left >= image.m_rect.right) + noBlit = true; + + if (destRect.bottom <= image.m_rect.top) + noBlit = true; + if (destRect.top >= image.m_rect.bottom) + noBlit = true; + + if (rowSizeBytes < 8) + packType = 1; + + const Rect imageRect = image.m_rect; + + const int componentSize = pixMapBE.m_componentSize; + + const int numScanLines = pixMapBE.m_bounds.bottom - pixMapBE.m_bounds.top; + const int scanLineWidth = pixMapBE.m_bounds.right - pixMapBE.m_bounds.left; + const int scanLineLeft = destRect.left; + const int scanLineTop = destRect.top; + + const int scanLineUsableWidth = std::min(scanLineWidth, static_cast(destRect.right - destRect.left)); + + if (!isDirect) + { + assert(packType == 0 || packType == 1); + } + + std::vector scanline; + scanline.resize(scanLineWidth); + + std::vector decompressedScanline; + if (packType == 1) + decompressedScanline.resize(rowSizeBytes); + + for (int lineNum = 0; lineNum < numScanLines; lineNum++) + { + if (packType == 0 || packType > 2) + { + // 8-bit RLE + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + if (packType == 3) + { + std::vector decompressed; + UnpackBits16(decompressed, compressedLine); + BlitLine16(decompressed, 0, &scanline[0], scanLineWidth); + } + else + { + std::vector decompressed; + UnpackBits8(decompressed, compressedLine); + + if (packType == 0) + BlitLineIndexed(decompressed, 0, &scanline[0], scanLineWidth, componentSize, colors); + else + { + assert(packType == 4); + BlitLineRGB8(decompressed, 0, &scanline[0], scanLineWidth); + } + } + } + else if (packType == 1) + { + if (stream.Read(&decompressedScanline[0], rowSizeBytes) != rowSizeBytes) + { + assert(false); + } + + if (componentSize == 1) + { + BlitLine1(decompressedScanline, 0, &scanline[0], scanLineWidth); + } + else if (componentSize == 8) + { + BlitLineIndexed(decompressedScanline, 0, &scanline[0], scanLineWidth, componentSize, colors); + } + else + { + assert(false); + } + } + else + { + assert(false); + } + + if (!noBlit) + { + const int yCoord = scanLineTop + lineNum; + const int localYCoord = yCoord - image.m_rect.top; + int localXCoordStart = scanLineLeft - image.m_rect.left; + int localXCoordEnd = scanLineLeft + scanLineUsableWidth; + + if (localYCoord < 0 || yCoord >= image.m_rect.bottom) + continue; + + size_t firstDestElement = localYCoord * image.m_width; + size_t firstSrcElement = 0; + size_t lastSrcElement = scanLineUsableWidth; + + if (localXCoordStart < 0) + { + firstSrcElement += static_cast(-localXCoordStart); + localXCoordStart = 0; + } + else if (localXCoordStart > 0) + { + firstDestElement += localXCoordStart; + } + + if (localXCoordEnd > image.m_rect.right) + { + const size_t extra = localXCoordEnd - image.m_rect.right; + lastSrcElement -= extra; + } + + const size_t numElements = lastSrcElement - firstSrcElement; + const size_t capacity = image.m_width * image.m_height; + + assert(firstDestElement <= capacity); + assert(capacity - firstDestElement >= numElements); + + memcpy(image.m_contents + firstDestElement, &scanline[firstSrcElement], sizeof(RGBAColor) * numElements); + } + } + + /* + BlitLineRGB8(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + else if (isDirect && packType == 3) + { + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits16(decompressed, compressedLine); + + BlitLine16(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols); + } + } + else if (packType <= 1) + { + for (int i = 0; i < numLines; i++) + { + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + stream.Read(&lineByteCount, 2); + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + stream.Read(&lineByteCountSmall, 1); + lineByteCount = lineByteCountSmall; + } + + std::vector compressedLine; + compressedLine.resize(lineByteCount); + + if (stream.Read(&compressedLine[0], lineByteCount) != lineByteCount) + { + assert(false); + } + + std::vector decompressed; + UnpackBits8(decompressed, compressedLine); + + BlitLineIndexed(decompressed, 0, image.m_contents + image.m_width * (firstRow + i) + firstCol, numCols, pixMap.m_componentSize, colors); + } + } + else + { + assert(false); + } + + */ + + // Either undocumented behavior or non-compliant PICT resources, not sure + if (isPixMap && (stream.Tell() & 1) != 0) + stream.SeekCurrent(1); +} + +class PictDumpEmitContext final : public QDPictEmitContext +{ +public: + bool SpecifyFrame(const Rect &rect) override + { + m_rect = rect; + m_width = rect.right - rect.left; + m_height = rect.bottom - rect.top; + m_image.resize(m_width * m_height); + return true; + } + + Rect ConstrainRegion(const Rect &rect) const override + { + return rect.Intersect(m_rect); + } + + void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) override + { + m_blitType = sourceType; + m_params = params; + m_constraintRegionWidth = params.m_constrainedRegionRight - params.m_constrainedRegionLeft; + m_constraintRegionStartIndex = params.m_constrainedRegionLeft - params.m_scanlineOriginX; + m_constraintRegionEndIndex = params.m_constrainedRegionRight - params.m_scanlineOriginX; + + const size_t firstCol = params.m_constrainedRegionLeft - m_rect.left; + const size_t firstRow = params.m_firstY - m_rect.top; + + m_outputIndexStart = firstRow * m_width + firstCol; + } + + void BlitScanlineAndAdvance(const void *data) override + { + const int32_t crRight = m_params.m_constrainedRegionRight; + const int32_t crLeft = m_params.m_constrainedRegionLeft; + const size_t constraintRegionStartIndex = m_constraintRegionStartIndex; + const uint8_t *dataBytes = static_cast(data); + const size_t outputIndexStart = m_outputIndexStart; + const RGBAColor *palette = m_params.m_colors; + const size_t planarSeparation = m_params.m_planarSeparation; + + static const RGBAColor bwColors[] = + { + { 255, 255, 255, 255 }, + { 0, 0, 0, 255 } + }; + + switch (m_blitType) + { + case QDPictBlitSourceType_Indexed1Bit: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 7 - (itemIndex & 7); + const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1; + m_image[i + outputIndexStart] = palette[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed2Bit: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 6 - (2 * (itemIndex & 1)); + const int colorIndex = (dataBytes[itemIndex / 4] >> bitShift) & 0x3; + m_image[i + outputIndexStart] = palette[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed4Bit: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 4 - (4 * (itemIndex & 1)); + const int colorIndex = (dataBytes[itemIndex / 2] >> bitShift) & 0xf; + m_image[i + outputIndexStart] = palette[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed8Bit: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + const uint8_t colorIndex = dataBytes[itemIndex]; + m_image[i + outputIndexStart] = palette[colorIndex]; + } + break; + case QDPictBlitSourceType_1Bit: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 7 - (itemIndex & 7); + const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1; + m_image[i + outputIndexStart] = bwColors[colorIndex]; + } + break; + case QDPictBlitSourceType_RGB15: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const uint16_t item = *reinterpret_cast(dataBytes + itemIndex * 2); + RGBAColor &outputItem = m_image[i + outputIndexStart]; + + outputItem.b = FiveToEight(item & 0x1f); + outputItem.g = FiveToEight((item >> 5) & 0x1f); + outputItem.r = FiveToEight((item >> 10) & 0x1f); + outputItem.a = 255; + } + break; + case QDPictBlitSourceType_RGB24_Multiplane: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + RGBAColor &outputItem = m_image[i + outputIndexStart]; + + outputItem.r = dataBytes[itemIndex]; + outputItem.g = dataBytes[itemIndex + planarSeparation]; + outputItem.b = dataBytes[itemIndex + planarSeparation * 2]; + outputItem.a = 255; + } + break; + default: + assert(false); + } + + m_outputIndexStart += m_width; + } + + bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) override + { + m_buffer1.resize(buffer1Size); + m_buffer2.resize(buffer2Size); + + buffer1 = &m_buffer1[0]; + buffer2 = &m_buffer2[0]; + + return true; + } + + void DumpImageToFile(const char *path) + { + stbi_write_png(path, static_cast(m_width), static_cast(m_height), 4, &m_image[0], static_cast(m_width * 4)); + } + +private: + Rect m_rect; + int32_t m_originX; + int32_t m_originY; + + size_t m_width; + size_t m_height; + size_t m_constraintRegionWidth; + size_t m_constraintRegionStartIndex; + size_t m_constraintRegionEndIndex; + size_t m_outputIndexStart; + QDPictBlitSourceType m_blitType; + QDPictEmitScanlineParameters m_params; + + std::vector m_buffer1; + std::vector m_buffer2; + + std::vector m_image; +}; + +void AuditPictOps2(const uint8_t *data, size_t sz, const char *dumpPath) +{ + MemReaderStream stream(data, sz); + + QDPictDecoder decoder; + PictDumpEmitContext emitContext; + + decoder.DecodePict(&stream, &emitContext); + + emitContext.DumpImageToFile(dumpPath); +} + +void AuditPictOps(const uint8_t *data, size_t sz, const char *dumpPath) +{ + MemReaderStream stream(data, sz); + + QDPictHeader header; + + header.Load(&stream); + + TempImage image; + image.m_width = header.GetFrame().right - header.GetFrame().left; + image.m_height = header.GetFrame().bottom - header.GetFrame().top; + image.m_rect = header.GetFrame(); + image.m_contents = new RGBAColor[image.m_width * image.m_height]; + + BERect scratchBERect; + Rect scratchRect; + BEUInt16_t scratchUInt16; + BEUInt32_t scratchUInt32; + + printf("Decode\n"); + + uint8_t scratchBytes[64]; + while (stream.Read(scratchBytes, header.GetVersion()) == header.GetVersion()) + { + bool finished = false; + + uint16_t opcode = scratchBytes[0]; + if (header.GetVersion() == 2) + opcode = (opcode << 8) | scratchBytes[1]; + + printf("Opcode: %x\n", opcode); + + switch (opcode) + { + case QDOpcodes::kNoop: + break; + case QDOpcodes::kClipRegion: + if (stream.Read(scratchBytes, 10) != 10 || scratchBytes[0] != 0 || scratchBytes[1] != 10) + return; // Unknown format region + + memcpy(&scratchBERect, scratchBytes + 2, 8); + scratchRect = scratchBERect.ToRect(); + + break; + case QDOpcodes::kShortComment: + stream.Read(scratchBytes, 2); + break; + case QDOpcodes::kLongComment: + stream.Read(scratchBytes, 2); + stream.Read(&scratchUInt16, 2); + stream.SeekCurrent(scratchUInt16); + break; + case QDOpcodes::kBitsRect: + AuditPackBitsRect(stream, header.GetVersion(), false, false, false, image); + break; + case QDOpcodes::kPackBitsRect: + AuditPackBitsRect(stream, header.GetVersion(), true, false, false, image); + break; + case QDOpcodes::kPackBitsRgn: + AuditPackBitsRect(stream, header.GetVersion(), true, true, false, image); + break; + case QDOpcodes::kDirectBitsRect: + AuditPackBitsRect(stream, header.GetVersion(), true, false, true, image); + break; + case QDOpcodes::kDirectBitsRgn: + AuditPackBitsRect(stream, header.GetVersion(), true, true, true, image); + break; + case QDOpcodes::kDefaultHilite: + break; + case QDOpcodes::kOpColor: + stream.SeekCurrent(6); + break; + //case QDOpcodes::kFrameRect: + // stream.SeekCurrent(8); + // break; + case QDOpcodes::kEndOfPicture: + finished = true; + break; + default: + assert(false); + } + + if (finished) + break; + } + + stbi_write_png(dumpPath, image.m_width, image.m_height, 4, image.m_contents, image.m_width * 4); + delete[] image.m_contents; +} + +int main(int argc, const char **argv) +{ + const char *files[] = + { + "Teddy World", "CD Demo House", "The Asylum Pro", "ApplicationResources", "California or Bust!", "Sampler", "Art Museum", "Castle o' the Air", + "Davis Station", "Demo House", "Empty House", "Fun House", "Grand Prix", "ImagineHouse PRO II", + "In The Mirror", "Land of Illusion", "Leviathan", "Metropolis", "Nemo's Market", + "Rainbow's End", "Slumberland", "SpacePods", "Titanic" + }; + + for (const char *filename : files) + { + std::string filePath = "D:\\Source Code\\GlidePort\\Packaged\\Houses\\"; + filePath += filename; + filePath += ".gpr"; + + FILE *f = fopen(filePath.c_str(), "rb"); + CFileStream stream(f); + + ResourceFile *resFile = new ResourceFile(); + if (!resFile->Load(&stream)) + { + assert(false); + } + stream.Close(); + + const ResourceCompiledTypeList *typeList = resFile->GetResourceTypeList('PICT'); + if (!typeList) + continue; + + const size_t numRefs = typeList->m_numRefs; + for (size_t i = 0; i < numRefs; i++) + { + const MMHandleBlock *hBlock = resFile->GetResource('PICT', typeList->m_firstRef[i].m_resID, true); + const void *pictData = hBlock->m_contents; + + std::string dumpPath = "D:\\Source Code\\GlidePort\\PictDump\\"; + + dumpPath += filename; + + char tempPath[128]; + sprintf(tempPath, "%i", static_cast(typeList->m_firstRef[i].m_resID)); + + dumpPath += " "; + dumpPath += tempPath; + dumpPath += ".png"; + + if (typeList->m_firstRef[i].m_resID == 10024) + { + int n = 0; + } + + AuditPictOps2(static_cast(pictData), hBlock->m_size, dumpPath.c_str()); + } + } + + return 0; +} diff --git a/PictChecker/PictChecker.vcxproj b/PictChecker/PictChecker.vcxproj new file mode 100644 index 0000000..0c36939 --- /dev/null +++ b/PictChecker/PictChecker.vcxproj @@ -0,0 +1,140 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {99549E56-2B3A-4B0C-9A1F-FBA6395BC96C} + PictChecker + 10.0.17763.0 + + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + Application + true + v141 + MultiByte + + + Application + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + Disabled + true + true + + + + + Level3 + MaxSpeed + true + true + true + true + + + true + true + + + + + + + + + {6ec62b0f-9353-40a4-a510-3788f1368b33} + + + + + + + + + \ No newline at end of file diff --git a/PictChecker/PictChecker.vcxproj.filters b/PictChecker/PictChecker.vcxproj.filters new file mode 100644 index 0000000..89cc874 --- /dev/null +++ b/PictChecker/PictChecker.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + + + Header Files + + + \ No newline at end of file diff --git a/PictChecker/stb_image_write.c b/PictChecker/stb_image_write.c new file mode 100644 index 0000000..9f7e750 --- /dev/null +++ b/PictChecker/stb_image_write.c @@ -0,0 +1,2 @@ +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "stb_image_write.h" diff --git a/PictChecker/stb_image_write.h b/PictChecker/stb_image_write.h new file mode 100644 index 0000000..8224740 --- /dev/null +++ b/PictChecker/stb_image_write.h @@ -0,0 +1,1619 @@ +/* stb_image_write - v1.13 - public domain - http://nothings.org/stb + writes out PNG/BMP/TGA/JPEG/HDR images to C stdio - Sean Barrett 2010-2015 + no warranty implied; use at your own risk + + Before #including, + + #define STB_IMAGE_WRITE_IMPLEMENTATION + + in the file that you want to have the implementation. + + Will probably not work correctly with strict-aliasing optimizations. + +ABOUT: + + This header file is a library for writing images to C stdio or a callback. + + The PNG output is not optimal; it is 20-50% larger than the file + written by a decent optimizing implementation; though providing a custom + zlib compress function (see STBIW_ZLIB_COMPRESS) can mitigate that. + This library is designed for source code compactness and simplicity, + not optimal image file size or run-time performance. + +BUILDING: + + You can #define STBIW_ASSERT(x) before the #include to avoid using assert.h. + You can #define STBIW_MALLOC(), STBIW_REALLOC(), and STBIW_FREE() to replace + malloc,realloc,free. + You can #define STBIW_MEMMOVE() to replace memmove() + You can #define STBIW_ZLIB_COMPRESS to use a custom zlib-style compress function + for PNG compression (instead of the builtin one), it must have the following signature: + unsigned char * my_compress(unsigned char *data, int data_len, int *out_len, int quality); + The returned data will be freed with STBIW_FREE() (free() by default), + so it must be heap allocated with STBIW_MALLOC() (malloc() by default), + +UNICODE: + + If compiling for Windows and you wish to use Unicode filenames, compile + with + #define STBIW_WINDOWS_UTF8 + and pass utf8-encoded filenames. Call stbiw_convert_wchar_to_utf8 to convert + Windows wchar_t filenames to utf8. + +USAGE: + + There are five functions, one for each image file format: + + int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); + int stbi_write_jpg(char const *filename, int w, int h, int comp, const void *data, int quality); + int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); + + void stbi_flip_vertically_on_write(int flag); // flag is non-zero to flip data vertically + + There are also five equivalent functions that use an arbitrary write function. You are + expected to open/close your file-equivalent before and after calling these: + + int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); + int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); + int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); + int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + + where the callback is: + void stbi_write_func(void *context, void *data, int size); + + You can configure it with these global variables: + int stbi_write_tga_with_rle; // defaults to true; set to 0 to disable RLE + int stbi_write_png_compression_level; // defaults to 8; set to higher for more compression + int stbi_write_force_png_filter; // defaults to -1; set to 0..5 to force a filter mode + + + You can define STBI_WRITE_NO_STDIO to disable the file variant of these + functions, so the library will not use stdio.h at all. However, this will + also disable HDR writing, because it requires stdio for formatted output. + + Each function returns 0 on failure and non-0 on success. + + The functions create an image file defined by the parameters. The image + is a rectangle of pixels stored from left-to-right, top-to-bottom. + Each pixel contains 'comp' channels of data stored interleaved with 8-bits + per channel, in the following order: 1=Y, 2=YA, 3=RGB, 4=RGBA. (Y is + monochrome color.) The rectangle is 'w' pixels wide and 'h' pixels tall. + The *data pointer points to the first byte of the top-left-most pixel. + For PNG, "stride_in_bytes" is the distance in bytes from the first byte of + a row of pixels to the first byte of the next row of pixels. + + PNG creates output files with the same number of components as the input. + The BMP format expands Y to RGB in the file format and does not + output alpha. + + PNG supports writing rectangles of data even when the bytes storing rows of + data are not consecutive in memory (e.g. sub-rectangles of a larger image), + by supplying the stride between the beginning of adjacent rows. The other + formats do not. (Thus you cannot write a native-format BMP through the BMP + writer, both because it is in BGR order and because it may have padding + at the end of the line.) + + PNG allows you to set the deflate compression level by setting the global + variable 'stbi_write_png_compression_level' (it defaults to 8). + + HDR expects linear float data. Since the format is always 32-bit rgb(e) + data, alpha (if provided) is discarded, and for monochrome data it is + replicated across all three channels. + + TGA supports RLE or non-RLE compressed data. To use non-RLE-compressed + data, set the global variable 'stbi_write_tga_with_rle' to 0. + + JPEG does ignore alpha channels in input data; quality is between 1 and 100. + Higher quality looks better but results in a bigger image. + JPEG baseline (no JPEG progressive). + +CREDITS: + + + Sean Barrett - PNG/BMP/TGA + Baldur Karlsson - HDR + Jean-Sebastien Guay - TGA monochrome + Tim Kelsey - misc enhancements + Alan Hickman - TGA RLE + Emmanuel Julien - initial file IO callback implementation + Jon Olick - original jo_jpeg.cpp code + Daniel Gibson - integrate JPEG, allow external zlib + Aarni Koskela - allow choosing PNG filter + + bugfixes: + github:Chribba + Guillaume Chereau + github:jry2 + github:romigrou + Sergio Gonzalez + Jonas Karlsson + Filip Wasil + Thatcher Ulrich + github:poppolopoppo + Patrick Boettcher + github:xeekworx + Cap Petschulat + Simon Rodriguez + Ivan Tikhonov + github:ignotion + Adam Schackart + +LICENSE + + See end of file for license information. + +*/ + +#ifndef INCLUDE_STB_IMAGE_WRITE_H +#define INCLUDE_STB_IMAGE_WRITE_H + +#include + +// if STB_IMAGE_WRITE_STATIC causes problems, try defining STBIWDEF to 'inline' or 'static inline' +#ifndef STBIWDEF +#ifdef STB_IMAGE_WRITE_STATIC +#define STBIWDEF static +#else +#ifdef __cplusplus +#define STBIWDEF extern "C" +#else +#define STBIWDEF extern +#endif +#endif +#endif + +#ifndef STB_IMAGE_WRITE_STATIC // C++ forbids static forward declarations +extern int stbi_write_tga_with_rle; +extern int stbi_write_png_compression_level; +extern int stbi_write_force_png_filter; +#endif + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga(char const *filename, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr(char const *filename, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality); + +#ifdef STBI_WINDOWS_UTF8 +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif +#endif + +typedef void stbi_write_func(void *context, void *data, int size); + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data, int stride_in_bytes); +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const void *data); +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int w, int h, int comp, const float *data); +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality); + +STBIWDEF void stbi_flip_vertically_on_write(int flip_boolean); + +#endif//INCLUDE_STB_IMAGE_WRITE_H + +#ifdef STB_IMAGE_WRITE_IMPLEMENTATION + +#ifdef _WIN32 + #ifndef _CRT_SECURE_NO_WARNINGS + #define _CRT_SECURE_NO_WARNINGS + #endif + #ifndef _CRT_NONSTDC_NO_DEPRECATE + #define _CRT_NONSTDC_NO_DEPRECATE + #endif +#endif + +#ifndef STBI_WRITE_NO_STDIO +#include +#endif // STBI_WRITE_NO_STDIO + +#include +#include +#include +#include + +#if defined(STBIW_MALLOC) && defined(STBIW_FREE) && (defined(STBIW_REALLOC) || defined(STBIW_REALLOC_SIZED)) +// ok +#elif !defined(STBIW_MALLOC) && !defined(STBIW_FREE) && !defined(STBIW_REALLOC) && !defined(STBIW_REALLOC_SIZED) +// ok +#else +#error "Must define all or none of STBIW_MALLOC, STBIW_FREE, and STBIW_REALLOC (or STBIW_REALLOC_SIZED)." +#endif + +#ifndef STBIW_MALLOC +#define STBIW_MALLOC(sz) malloc(sz) +#define STBIW_REALLOC(p,newsz) realloc(p,newsz) +#define STBIW_FREE(p) free(p) +#endif + +#ifndef STBIW_REALLOC_SIZED +#define STBIW_REALLOC_SIZED(p,oldsz,newsz) STBIW_REALLOC(p,newsz) +#endif + + +#ifndef STBIW_MEMMOVE +#define STBIW_MEMMOVE(a,b,sz) memmove(a,b,sz) +#endif + + +#ifndef STBIW_ASSERT +#include +#define STBIW_ASSERT(x) assert(x) +#endif + +#define STBIW_UCHAR(x) (unsigned char) ((x) & 0xff) + +#ifdef STB_IMAGE_WRITE_STATIC +static int stbi__flip_vertically_on_write=0; +static int stbi_write_png_compression_level = 8; +static int stbi_write_tga_with_rle = 1; +static int stbi_write_force_png_filter = -1; +#else +int stbi_write_png_compression_level = 8; +int stbi__flip_vertically_on_write=0; +int stbi_write_tga_with_rle = 1; +int stbi_write_force_png_filter = -1; +#endif + +STBIWDEF void stbi_flip_vertically_on_write(int flag) +{ + stbi__flip_vertically_on_write = flag; +} + +typedef struct +{ + stbi_write_func *func; + void *context; +} stbi__write_context; + +// initialize a callback-based context +static void stbi__start_write_callbacks(stbi__write_context *s, stbi_write_func *c, void *context) +{ + s->func = c; + s->context = context; +} + +#ifndef STBI_WRITE_NO_STDIO + +static void stbi__stdio_write(void *context, void *data, int size) +{ + fwrite(data,1,size,(FILE*) context); +} + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +#ifdef __cplusplus +#define STBIW_EXTERN extern "C" +#else +#define STBIW_EXTERN extern +#endif +STBIW_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBIW_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); + +STBIWDEF int stbiw_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + +static FILE *stbiw__fopen(char const *filename, char const *mode) +{ + FILE *f; +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 + if (0 != fopen_s(&f, filename, mode)) + f=0; +#else + f = fopen(filename, mode); +#endif + return f; +} + +static int stbi__start_write_file(stbi__write_context *s, const char *filename) +{ + FILE *f = stbiw__fopen(filename, "wb"); + stbi__start_write_callbacks(s, stbi__stdio_write, (void *) f); + return f != NULL; +} + +static void stbi__end_write_file(stbi__write_context *s) +{ + fclose((FILE *)s->context); +} + +#endif // !STBI_WRITE_NO_STDIO + +typedef unsigned int stbiw_uint32; +typedef int stb_image_write_test[sizeof(stbiw_uint32)==4 ? 1 : -1]; + +static void stbiw__writefv(stbi__write_context *s, const char *fmt, va_list v) +{ + while (*fmt) { + switch (*fmt++) { + case ' ': break; + case '1': { unsigned char x = STBIW_UCHAR(va_arg(v, int)); + s->func(s->context,&x,1); + break; } + case '2': { int x = va_arg(v,int); + unsigned char b[2]; + b[0] = STBIW_UCHAR(x); + b[1] = STBIW_UCHAR(x>>8); + s->func(s->context,b,2); + break; } + case '4': { stbiw_uint32 x = va_arg(v,int); + unsigned char b[4]; + b[0]=STBIW_UCHAR(x); + b[1]=STBIW_UCHAR(x>>8); + b[2]=STBIW_UCHAR(x>>16); + b[3]=STBIW_UCHAR(x>>24); + s->func(s->context,b,4); + break; } + default: + STBIW_ASSERT(0); + return; + } + } +} + +static void stbiw__writef(stbi__write_context *s, const char *fmt, ...) +{ + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); +} + +static void stbiw__putc(stbi__write_context *s, unsigned char c) +{ + s->func(s->context, &c, 1); +} + +static void stbiw__write3(stbi__write_context *s, unsigned char a, unsigned char b, unsigned char c) +{ + unsigned char arr[3]; + arr[0] = a; arr[1] = b; arr[2] = c; + s->func(s->context, arr, 3); +} + +static void stbiw__write_pixel(stbi__write_context *s, int rgb_dir, int comp, int write_alpha, int expand_mono, unsigned char *d) +{ + unsigned char bg[3] = { 255, 0, 255}, px[3]; + int k; + + if (write_alpha < 0) + s->func(s->context, &d[comp - 1], 1); + + switch (comp) { + case 2: // 2 pixels = mono + alpha, alpha is written separately, so same as 1-channel case + case 1: + if (expand_mono) + stbiw__write3(s, d[0], d[0], d[0]); // monochrome bmp + else + s->func(s->context, d, 1); // monochrome TGA + break; + case 4: + if (!write_alpha) { + // composite against pink background + for (k = 0; k < 3; ++k) + px[k] = bg[k] + ((d[k] - bg[k]) * d[3]) / 255; + stbiw__write3(s, px[1 - rgb_dir], px[1], px[1 + rgb_dir]); + break; + } + /* FALLTHROUGH */ + case 3: + stbiw__write3(s, d[1 - rgb_dir], d[1], d[1 + rgb_dir]); + break; + } + if (write_alpha > 0) + s->func(s->context, &d[comp - 1], 1); +} + +static void stbiw__write_pixels(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, void *data, int write_alpha, int scanline_pad, int expand_mono) +{ + stbiw_uint32 zero = 0; + int i,j, j_end; + + if (y <= 0) + return; + + if (stbi__flip_vertically_on_write) + vdir *= -1; + + if (vdir < 0) { + j_end = -1; j = y-1; + } else { + j_end = y; j = 0; + } + + for (; j != j_end; j += vdir) { + for (i=0; i < x; ++i) { + unsigned char *d = (unsigned char *) data + (j*x+i)*comp; + stbiw__write_pixel(s, rgb_dir, comp, write_alpha, expand_mono, d); + } + s->func(s->context, &zero, scanline_pad); + } +} + +static int stbiw__outfile(stbi__write_context *s, int rgb_dir, int vdir, int x, int y, int comp, int expand_mono, void *data, int alpha, int pad, const char *fmt, ...) +{ + if (y < 0 || x < 0) { + return 0; + } else { + va_list v; + va_start(v, fmt); + stbiw__writefv(s, fmt, v); + va_end(v); + stbiw__write_pixels(s,rgb_dir,vdir,x,y,comp,data,alpha,pad, expand_mono); + return 1; + } +} + +static int stbi_write_bmp_core(stbi__write_context *s, int x, int y, int comp, const void *data) +{ + int pad = (-x*3) & 3; + return stbiw__outfile(s,-1,-1,x,y,comp,1,(void *) data,0,pad, + "11 4 22 4" "4 44 22 444444", + 'B', 'M', 14+40+(x*3+pad)*y, 0,0, 14+40, // file header + 40, x,y, 1,24, 0,0,0,0,0,0); // bitmap header +} + +STBIWDEF int stbi_write_bmp_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_bmp_core(&s, x, y, comp, data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_bmp(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_bmp_core(&s, x, y, comp, data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif //!STBI_WRITE_NO_STDIO + +static int stbi_write_tga_core(stbi__write_context *s, int x, int y, int comp, void *data) +{ + int has_alpha = (comp == 2 || comp == 4); + int colorbytes = has_alpha ? comp-1 : comp; + int format = colorbytes < 2 ? 3 : 2; // 3 color channels (RGB/RGBA) = 2, 1 color channel (Y/YA) = 3 + + if (y < 0 || x < 0) + return 0; + + if (!stbi_write_tga_with_rle) { + return stbiw__outfile(s, -1, -1, x, y, comp, 0, (void *) data, has_alpha, 0, + "111 221 2222 11", 0, 0, format, 0, 0, 0, 0, 0, x, y, (colorbytes + has_alpha) * 8, has_alpha * 8); + } else { + int i,j,k; + int jend, jdir; + + stbiw__writef(s, "111 221 2222 11", 0,0,format+8, 0,0,0, 0,0,x,y, (colorbytes + has_alpha) * 8, has_alpha * 8); + + if (stbi__flip_vertically_on_write) { + j = 0; + jend = y; + jdir = 1; + } else { + j = y-1; + jend = -1; + jdir = -1; + } + for (; j != jend; j += jdir) { + unsigned char *row = (unsigned char *) data + j * x * comp; + int len; + + for (i = 0; i < x; i += len) { + unsigned char *begin = row + i * comp; + int diff = 1; + len = 1; + + if (i < x - 1) { + ++len; + diff = memcmp(begin, row + (i + 1) * comp, comp); + if (diff) { + const unsigned char *prev = begin; + for (k = i + 2; k < x && len < 128; ++k) { + if (memcmp(prev, row + k * comp, comp)) { + prev += comp; + ++len; + } else { + --len; + break; + } + } + } else { + for (k = i + 2; k < x && len < 128; ++k) { + if (!memcmp(begin, row + k * comp, comp)) { + ++len; + } else { + break; + } + } + } + } + + if (diff) { + unsigned char header = STBIW_UCHAR(len - 1); + s->func(s->context, &header, 1); + for (k = 0; k < len; ++k) { + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin + k * comp); + } + } else { + unsigned char header = STBIW_UCHAR(len - 129); + s->func(s->context, &header, 1); + stbiw__write_pixel(s, -1, comp, has_alpha, 0, begin); + } + } + } + } + return 1; +} + +STBIWDEF int stbi_write_tga_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_tga_core(&s, x, y, comp, (void *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_tga(char const *filename, int x, int y, int comp, const void *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_tga_core(&s, x, y, comp, (void *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +// ************************************************************************************************* +// Radiance RGBE HDR writer +// by Baldur Karlsson + +#define stbiw__max(a, b) ((a) > (b) ? (a) : (b)) + +static void stbiw__linear_to_rgbe(unsigned char *rgbe, float *linear) +{ + int exponent; + float maxcomp = stbiw__max(linear[0], stbiw__max(linear[1], linear[2])); + + if (maxcomp < 1e-32f) { + rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0; + } else { + float normalize = (float) frexp(maxcomp, &exponent) * 256.0f/maxcomp; + + rgbe[0] = (unsigned char)(linear[0] * normalize); + rgbe[1] = (unsigned char)(linear[1] * normalize); + rgbe[2] = (unsigned char)(linear[2] * normalize); + rgbe[3] = (unsigned char)(exponent + 128); + } +} + +static void stbiw__write_run_data(stbi__write_context *s, int length, unsigned char databyte) +{ + unsigned char lengthbyte = STBIW_UCHAR(length+128); + STBIW_ASSERT(length+128 <= 255); + s->func(s->context, &lengthbyte, 1); + s->func(s->context, &databyte, 1); +} + +static void stbiw__write_dump_data(stbi__write_context *s, int length, unsigned char *data) +{ + unsigned char lengthbyte = STBIW_UCHAR(length); + STBIW_ASSERT(length <= 128); // inconsistent with spec but consistent with official code + s->func(s->context, &lengthbyte, 1); + s->func(s->context, data, length); +} + +static void stbiw__write_hdr_scanline(stbi__write_context *s, int width, int ncomp, unsigned char *scratch, float *scanline) +{ + unsigned char scanlineheader[4] = { 2, 2, 0, 0 }; + unsigned char rgbe[4]; + float linear[3]; + int x; + + scanlineheader[2] = (width&0xff00)>>8; + scanlineheader[3] = (width&0x00ff); + + /* skip RLE for images too small or large */ + if (width < 8 || width >= 32768) { + for (x=0; x < width; x++) { + switch (ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + s->func(s->context, rgbe, 4); + } + } else { + int c,r; + /* encode into scratch buffer */ + for (x=0; x < width; x++) { + switch(ncomp) { + case 4: /* fallthrough */ + case 3: linear[2] = scanline[x*ncomp + 2]; + linear[1] = scanline[x*ncomp + 1]; + linear[0] = scanline[x*ncomp + 0]; + break; + default: + linear[0] = linear[1] = linear[2] = scanline[x*ncomp + 0]; + break; + } + stbiw__linear_to_rgbe(rgbe, linear); + scratch[x + width*0] = rgbe[0]; + scratch[x + width*1] = rgbe[1]; + scratch[x + width*2] = rgbe[2]; + scratch[x + width*3] = rgbe[3]; + } + + s->func(s->context, scanlineheader, 4); + + /* RLE each component separately */ + for (c=0; c < 4; c++) { + unsigned char *comp = &scratch[width*c]; + + x = 0; + while (x < width) { + // find first run + r = x; + while (r+2 < width) { + if (comp[r] == comp[r+1] && comp[r] == comp[r+2]) + break; + ++r; + } + if (r+2 >= width) + r = width; + // dump up to first run + while (x < r) { + int len = r-x; + if (len > 128) len = 128; + stbiw__write_dump_data(s, len, &comp[x]); + x += len; + } + // if there's a run, output it + if (r+2 < width) { // same test as what we break out of in search loop, so only true if we break'd + // find next byte after run + while (r < width && comp[r] == comp[x]) + ++r; + // output run up to r + while (x < r) { + int len = r-x; + if (len > 127) len = 127; + stbiw__write_run_data(s, len, comp[x]); + x += len; + } + } + } + } + } +} + +static int stbi_write_hdr_core(stbi__write_context *s, int x, int y, int comp, float *data) +{ + if (y <= 0 || x <= 0 || data == NULL) + return 0; + else { + // Each component is stored separately. Allocate scratch space for full output scanline. + unsigned char *scratch = (unsigned char *) STBIW_MALLOC(x*4); + int i, len; + char buffer[128]; + char header[] = "#?RADIANCE\n# Written by stb_image_write.h\nFORMAT=32-bit_rle_rgbe\n"; + s->func(s->context, header, sizeof(header)-1); + +#ifdef __STDC_WANT_SECURE_LIB__ + len = sprintf_s(buffer, sizeof(buffer), "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#else + len = sprintf(buffer, "EXPOSURE= 1.0000000000000\n\n-Y %d +X %d\n", y, x); +#endif + s->func(s->context, buffer, len); + + for(i=0; i < y; i++) + stbiw__write_hdr_scanline(s, x, comp, scratch, data + comp*x*(stbi__flip_vertically_on_write ? y-1-i : i)); + STBIW_FREE(scratch); + return 1; + } +} + +STBIWDEF int stbi_write_hdr_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_hdr_core(&s, x, y, comp, (float *) data); +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_hdr(char const *filename, int x, int y, int comp, const float *data) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_hdr_core(&s, x, y, comp, (float *) data); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif // STBI_WRITE_NO_STDIO + + +////////////////////////////////////////////////////////////////////////////// +// +// PNG writer +// + +#ifndef STBIW_ZLIB_COMPRESS +// stretchy buffer; stbiw__sbpush() == vector<>::push_back() -- stbiw__sbcount() == vector<>::size() +#define stbiw__sbraw(a) ((int *) (a) - 2) +#define stbiw__sbm(a) stbiw__sbraw(a)[0] +#define stbiw__sbn(a) stbiw__sbraw(a)[1] + +#define stbiw__sbneedgrow(a,n) ((a)==0 || stbiw__sbn(a)+n >= stbiw__sbm(a)) +#define stbiw__sbmaybegrow(a,n) (stbiw__sbneedgrow(a,(n)) ? stbiw__sbgrow(a,n) : 0) +#define stbiw__sbgrow(a,n) stbiw__sbgrowf((void **) &(a), (n), sizeof(*(a))) + +#define stbiw__sbpush(a, v) (stbiw__sbmaybegrow(a,1), (a)[stbiw__sbn(a)++] = (v)) +#define stbiw__sbcount(a) ((a) ? stbiw__sbn(a) : 0) +#define stbiw__sbfree(a) ((a) ? STBIW_FREE(stbiw__sbraw(a)),0 : 0) + +static void *stbiw__sbgrowf(void **arr, int increment, int itemsize) +{ + int m = *arr ? 2*stbiw__sbm(*arr)+increment : increment+1; + void *p = STBIW_REALLOC_SIZED(*arr ? stbiw__sbraw(*arr) : 0, *arr ? (stbiw__sbm(*arr)*itemsize + sizeof(int)*2) : 0, itemsize * m + sizeof(int)*2); + STBIW_ASSERT(p); + if (p) { + if (!*arr) ((int *) p)[1] = 0; + *arr = (void *) ((int *) p + 2); + stbiw__sbm(*arr) = m; + } + return *arr; +} + +static unsigned char *stbiw__zlib_flushf(unsigned char *data, unsigned int *bitbuffer, int *bitcount) +{ + while (*bitcount >= 8) { + stbiw__sbpush(data, STBIW_UCHAR(*bitbuffer)); + *bitbuffer >>= 8; + *bitcount -= 8; + } + return data; +} + +static int stbiw__zlib_bitrev(int code, int codebits) +{ + int res=0; + while (codebits--) { + res = (res << 1) | (code & 1); + code >>= 1; + } + return res; +} + +static unsigned int stbiw__zlib_countm(unsigned char *a, unsigned char *b, int limit) +{ + int i; + for (i=0; i < limit && i < 258; ++i) + if (a[i] != b[i]) break; + return i; +} + +static unsigned int stbiw__zhash(unsigned char *data) +{ + stbiw_uint32 hash = data[0] + (data[1] << 8) + (data[2] << 16); + hash ^= hash << 3; + hash += hash >> 5; + hash ^= hash << 4; + hash += hash >> 17; + hash ^= hash << 25; + hash += hash >> 6; + return hash; +} + +#define stbiw__zlib_flush() (out = stbiw__zlib_flushf(out, &bitbuf, &bitcount)) +#define stbiw__zlib_add(code,codebits) \ + (bitbuf |= (code) << bitcount, bitcount += (codebits), stbiw__zlib_flush()) +#define stbiw__zlib_huffa(b,c) stbiw__zlib_add(stbiw__zlib_bitrev(b,c),c) +// default huffman tables +#define stbiw__zlib_huff1(n) stbiw__zlib_huffa(0x30 + (n), 8) +#define stbiw__zlib_huff2(n) stbiw__zlib_huffa(0x190 + (n)-144, 9) +#define stbiw__zlib_huff3(n) stbiw__zlib_huffa(0 + (n)-256,7) +#define stbiw__zlib_huff4(n) stbiw__zlib_huffa(0xc0 + (n)-280,8) +#define stbiw__zlib_huff(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : (n) <= 255 ? stbiw__zlib_huff2(n) : (n) <= 279 ? stbiw__zlib_huff3(n) : stbiw__zlib_huff4(n)) +#define stbiw__zlib_huffb(n) ((n) <= 143 ? stbiw__zlib_huff1(n) : stbiw__zlib_huff2(n)) + +#define stbiw__ZHASH 16384 + +#endif // STBIW_ZLIB_COMPRESS + +STBIWDEF unsigned char * stbi_zlib_compress(unsigned char *data, int data_len, int *out_len, int quality) +{ +#ifdef STBIW_ZLIB_COMPRESS + // user provided a zlib compress implementation, use that + return STBIW_ZLIB_COMPRESS(data, data_len, out_len, quality); +#else // use builtin + static unsigned short lengthc[] = { 3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258, 259 }; + static unsigned char lengtheb[]= { 0,0,0,0,0,0,0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 }; + static unsigned short distc[] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577, 32768 }; + static unsigned char disteb[] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13 }; + unsigned int bitbuf=0; + int i,j, bitcount=0; + unsigned char *out = NULL; + unsigned char ***hash_table = (unsigned char***) STBIW_MALLOC(stbiw__ZHASH * sizeof(unsigned char**)); + if (hash_table == NULL) + return NULL; + if (quality < 5) quality = 5; + + stbiw__sbpush(out, 0x78); // DEFLATE 32K window + stbiw__sbpush(out, 0x5e); // FLEVEL = 1 + stbiw__zlib_add(1,1); // BFINAL = 1 + stbiw__zlib_add(1,2); // BTYPE = 1 -- fixed huffman + + for (i=0; i < stbiw__ZHASH; ++i) + hash_table[i] = NULL; + + i=0; + while (i < data_len-3) { + // hash next 3 bytes of data to be compressed + int h = stbiw__zhash(data+i)&(stbiw__ZHASH-1), best=3; + unsigned char *bestloc = 0; + unsigned char **hlist = hash_table[h]; + int n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32768) { // if entry lies within window + int d = stbiw__zlib_countm(hlist[j], data+i, data_len-i); + if (d >= best) { best=d; bestloc=hlist[j]; } + } + } + // when hash table entry is too long, delete half the entries + if (hash_table[h] && stbiw__sbn(hash_table[h]) == 2*quality) { + STBIW_MEMMOVE(hash_table[h], hash_table[h]+quality, sizeof(hash_table[h][0])*quality); + stbiw__sbn(hash_table[h]) = quality; + } + stbiw__sbpush(hash_table[h],data+i); + + if (bestloc) { + // "lazy matching" - check match at *next* byte, and if it's better, do cur byte as literal + h = stbiw__zhash(data+i+1)&(stbiw__ZHASH-1); + hlist = hash_table[h]; + n = stbiw__sbcount(hlist); + for (j=0; j < n; ++j) { + if (hlist[j]-data > i-32767) { + int e = stbiw__zlib_countm(hlist[j], data+i+1, data_len-i-1); + if (e > best) { // if next match is better, bail on current match + bestloc = NULL; + break; + } + } + } + } + + if (bestloc) { + int d = (int) (data+i - bestloc); // distance back + STBIW_ASSERT(d <= 32767 && best <= 258); + for (j=0; best > lengthc[j+1]-1; ++j); + stbiw__zlib_huff(j+257); + if (lengtheb[j]) stbiw__zlib_add(best - lengthc[j], lengtheb[j]); + for (j=0; d > distc[j+1]-1; ++j); + stbiw__zlib_add(stbiw__zlib_bitrev(j,5),5); + if (disteb[j]) stbiw__zlib_add(d - distc[j], disteb[j]); + i += best; + } else { + stbiw__zlib_huffb(data[i]); + ++i; + } + } + // write out final bytes + for (;i < data_len; ++i) + stbiw__zlib_huffb(data[i]); + stbiw__zlib_huff(256); // end of block + // pad with 0 bits to byte boundary + while (bitcount) + stbiw__zlib_add(0,1); + + for (i=0; i < stbiw__ZHASH; ++i) + (void) stbiw__sbfree(hash_table[i]); + STBIW_FREE(hash_table); + + { + // compute adler32 on input + unsigned int s1=1, s2=0; + int blocklen = (int) (data_len % 5552); + j=0; + while (j < data_len) { + for (i=0; i < blocklen; ++i) { s1 += data[j+i]; s2 += s1; } + s1 %= 65521; s2 %= 65521; + j += blocklen; + blocklen = 5552; + } + stbiw__sbpush(out, STBIW_UCHAR(s2 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s2)); + stbiw__sbpush(out, STBIW_UCHAR(s1 >> 8)); + stbiw__sbpush(out, STBIW_UCHAR(s1)); + } + *out_len = stbiw__sbn(out); + // make returned pointer freeable + STBIW_MEMMOVE(stbiw__sbraw(out), out, *out_len); + return (unsigned char *) stbiw__sbraw(out); +#endif // STBIW_ZLIB_COMPRESS +} + +static unsigned int stbiw__crc32(unsigned char *buffer, int len) +{ +#ifdef STBIW_CRC32 + return STBIW_CRC32(buffer, len); +#else + static unsigned int crc_table[256] = + { + 0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, + 0x0eDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, + 0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, + 0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, + 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, + 0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, + 0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, + 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, + 0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, + 0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, + 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, + 0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, + 0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, + 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, + 0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, + 0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, + 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, + 0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, + 0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, + 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, + 0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, + 0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, + 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, + 0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, + 0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, + 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, + 0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, + 0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, + 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, + 0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, + 0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, + 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D + }; + + unsigned int crc = ~0u; + int i; + for (i=0; i < len; ++i) + crc = (crc >> 8) ^ crc_table[buffer[i] ^ (crc & 0xff)]; + return ~crc; +#endif +} + +#define stbiw__wpng4(o,a,b,c,d) ((o)[0]=STBIW_UCHAR(a),(o)[1]=STBIW_UCHAR(b),(o)[2]=STBIW_UCHAR(c),(o)[3]=STBIW_UCHAR(d),(o)+=4) +#define stbiw__wp32(data,v) stbiw__wpng4(data, (v)>>24,(v)>>16,(v)>>8,(v)); +#define stbiw__wptag(data,s) stbiw__wpng4(data, s[0],s[1],s[2],s[3]) + +static void stbiw__wpcrc(unsigned char **data, int len) +{ + unsigned int crc = stbiw__crc32(*data - len - 4, len+4); + stbiw__wp32(*data, crc); +} + +static unsigned char stbiw__paeth(int a, int b, int c) +{ + int p = a + b - c, pa = abs(p-a), pb = abs(p-b), pc = abs(p-c); + if (pa <= pb && pa <= pc) return STBIW_UCHAR(a); + if (pb <= pc) return STBIW_UCHAR(b); + return STBIW_UCHAR(c); +} + +// @OPTIMIZE: provide an option that always forces left-predict or paeth predict +static void stbiw__encode_png_line(unsigned char *pixels, int stride_bytes, int width, int height, int y, int n, int filter_type, signed char *line_buffer) +{ + static int mapping[] = { 0,1,2,3,4 }; + static int firstmap[] = { 0,1,0,5,6 }; + int *mymap = (y != 0) ? mapping : firstmap; + int i; + int type = mymap[filter_type]; + unsigned char *z = pixels + stride_bytes * (stbi__flip_vertically_on_write ? height-1-y : y); + int signed_stride = stbi__flip_vertically_on_write ? -stride_bytes : stride_bytes; + + if (type==0) { + memcpy(line_buffer, z, width*n); + return; + } + + // first loop isn't optimized since it's just one pixel + for (i = 0; i < n; ++i) { + switch (type) { + case 1: line_buffer[i] = z[i]; break; + case 2: line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: line_buffer[i] = z[i] - (z[i-signed_stride]>>1); break; + case 4: line_buffer[i] = (signed char) (z[i] - stbiw__paeth(0,z[i-signed_stride],0)); break; + case 5: line_buffer[i] = z[i]; break; + case 6: line_buffer[i] = z[i]; break; + } + } + switch (type) { + case 1: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-n]; break; + case 2: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - z[i-signed_stride]; break; + case 3: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - ((z[i-n] + z[i-signed_stride])>>1); break; + case 4: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], z[i-signed_stride], z[i-signed_stride-n]); break; + case 5: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - (z[i-n]>>1); break; + case 6: for (i=n; i < width*n; ++i) line_buffer[i] = z[i] - stbiw__paeth(z[i-n], 0,0); break; + } +} + +STBIWDEF unsigned char *stbi_write_png_to_mem(const unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len) +{ + int force_filter = stbi_write_force_png_filter; + int ctype[5] = { -1, 0, 4, 2, 6 }; + unsigned char sig[8] = { 137,80,78,71,13,10,26,10 }; + unsigned char *out,*o, *filt, *zlib; + signed char *line_buffer; + int j,zlen; + + if (stride_bytes == 0) + stride_bytes = x * n; + + if (force_filter >= 5) { + force_filter = -1; + } + + filt = (unsigned char *) STBIW_MALLOC((x*n+1) * y); if (!filt) return 0; + line_buffer = (signed char *) STBIW_MALLOC(x * n); if (!line_buffer) { STBIW_FREE(filt); return 0; } + for (j=0; j < y; ++j) { + int filter_type; + if (force_filter > -1) { + filter_type = force_filter; + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, force_filter, line_buffer); + } else { // Estimate the best filter by running through all of them: + int best_filter = 0, best_filter_val = 0x7fffffff, est, i; + for (filter_type = 0; filter_type < 5; filter_type++) { + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, filter_type, line_buffer); + + // Estimate the entropy of the line using this filter; the less, the better. + est = 0; + for (i = 0; i < x*n; ++i) { + est += abs((signed char) line_buffer[i]); + } + if (est < best_filter_val) { + best_filter_val = est; + best_filter = filter_type; + } + } + if (filter_type != best_filter) { // If the last iteration already got us the best filter, don't redo it + stbiw__encode_png_line((unsigned char*)(pixels), stride_bytes, x, y, j, n, best_filter, line_buffer); + filter_type = best_filter; + } + } + // when we get here, filter_type contains the filter type, and line_buffer contains the data + filt[j*(x*n+1)] = (unsigned char) filter_type; + STBIW_MEMMOVE(filt+j*(x*n+1)+1, line_buffer, x*n); + } + STBIW_FREE(line_buffer); + zlib = stbi_zlib_compress(filt, y*( x*n+1), &zlen, stbi_write_png_compression_level); + STBIW_FREE(filt); + if (!zlib) return 0; + + // each tag requires 12 bytes of overhead + out = (unsigned char *) STBIW_MALLOC(8 + 12+13 + 12+zlen + 12); + if (!out) return 0; + *out_len = 8 + 12+13 + 12+zlen + 12; + + o=out; + STBIW_MEMMOVE(o,sig,8); o+= 8; + stbiw__wp32(o, 13); // header length + stbiw__wptag(o, "IHDR"); + stbiw__wp32(o, x); + stbiw__wp32(o, y); + *o++ = 8; + *o++ = STBIW_UCHAR(ctype[n]); + *o++ = 0; + *o++ = 0; + *o++ = 0; + stbiw__wpcrc(&o,13); + + stbiw__wp32(o, zlen); + stbiw__wptag(o, "IDAT"); + STBIW_MEMMOVE(o, zlib, zlen); + o += zlen; + STBIW_FREE(zlib); + stbiw__wpcrc(&o, zlen); + + stbiw__wp32(o,0); + stbiw__wptag(o, "IEND"); + stbiw__wpcrc(&o,0); + + STBIW_ASSERT(o == out + *out_len); + + return out; +} + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_png(char const *filename, int x, int y, int comp, const void *data, int stride_bytes) +{ + FILE *f; + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + + f = stbiw__fopen(filename, "wb"); + if (!f) { STBIW_FREE(png); return 0; } + fwrite(png, 1, len, f); + fclose(f); + STBIW_FREE(png); + return 1; +} +#endif + +STBIWDEF int stbi_write_png_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int stride_bytes) +{ + int len; + unsigned char *png = stbi_write_png_to_mem((const unsigned char *) data, stride_bytes, x, y, comp, &len); + if (png == NULL) return 0; + func(context, png, len); + STBIW_FREE(png); + return 1; +} + + +/* *************************************************************************** + * + * JPEG writer + * + * This is based on Jon Olick's jo_jpeg.cpp: + * public domain Simple, Minimalistic JPEG writer - http://www.jonolick.com/code.html + */ + +static const unsigned char stbiw__jpg_ZigZag[] = { 0,1,5,6,14,15,27,28,2,4,7,13,16,26,29,42,3,8,12,17,25,30,41,43,9,11,18, + 24,31,40,44,53,10,19,23,32,39,45,52,54,20,22,33,38,46,51,55,60,21,34,37,47,50,56,59,61,35,36,48,49,57,58,62,63 }; + +static void stbiw__jpg_writeBits(stbi__write_context *s, int *bitBufP, int *bitCntP, const unsigned short *bs) { + int bitBuf = *bitBufP, bitCnt = *bitCntP; + bitCnt += bs[1]; + bitBuf |= bs[0] << (24 - bitCnt); + while(bitCnt >= 8) { + unsigned char c = (bitBuf >> 16) & 255; + stbiw__putc(s, c); + if(c == 255) { + stbiw__putc(s, 0); + } + bitBuf <<= 8; + bitCnt -= 8; + } + *bitBufP = bitBuf; + *bitCntP = bitCnt; +} + +static void stbiw__jpg_DCT(float *d0p, float *d1p, float *d2p, float *d3p, float *d4p, float *d5p, float *d6p, float *d7p) { + float d0 = *d0p, d1 = *d1p, d2 = *d2p, d3 = *d3p, d4 = *d4p, d5 = *d5p, d6 = *d6p, d7 = *d7p; + float z1, z2, z3, z4, z5, z11, z13; + + float tmp0 = d0 + d7; + float tmp7 = d0 - d7; + float tmp1 = d1 + d6; + float tmp6 = d1 - d6; + float tmp2 = d2 + d5; + float tmp5 = d2 - d5; + float tmp3 = d3 + d4; + float tmp4 = d3 - d4; + + // Even part + float tmp10 = tmp0 + tmp3; // phase 2 + float tmp13 = tmp0 - tmp3; + float tmp11 = tmp1 + tmp2; + float tmp12 = tmp1 - tmp2; + + d0 = tmp10 + tmp11; // phase 3 + d4 = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * 0.707106781f; // c4 + d2 = tmp13 + z1; // phase 5 + d6 = tmp13 - z1; + + // Odd part + tmp10 = tmp4 + tmp5; // phase 2 + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + // The rotator is modified from fig 4-8 to avoid extra negations. + z5 = (tmp10 - tmp12) * 0.382683433f; // c6 + z2 = tmp10 * 0.541196100f + z5; // c2-c6 + z4 = tmp12 * 1.306562965f + z5; // c2+c6 + z3 = tmp11 * 0.707106781f; // c4 + + z11 = tmp7 + z3; // phase 5 + z13 = tmp7 - z3; + + *d5p = z13 + z2; // phase 6 + *d3p = z13 - z2; + *d1p = z11 + z4; + *d7p = z11 - z4; + + *d0p = d0; *d2p = d2; *d4p = d4; *d6p = d6; +} + +static void stbiw__jpg_calcBits(int val, unsigned short bits[2]) { + int tmp1 = val < 0 ? -val : val; + val = val < 0 ? val-1 : val; + bits[1] = 1; + while(tmp1 >>= 1) { + ++bits[1]; + } + bits[0] = val & ((1<0)&&(DU[end0pos]==0); --end0pos) { + } + // end0pos = first element in reverse order !=0 + if(end0pos == 0) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + return DU[0]; + } + for(i = 1; i <= end0pos; ++i) { + int startpos = i; + int nrzeroes; + unsigned short bits[2]; + for (; DU[i]==0 && i<=end0pos; ++i) { + } + nrzeroes = i-startpos; + if ( nrzeroes >= 16 ) { + int lng = nrzeroes>>4; + int nrmarker; + for (nrmarker=1; nrmarker <= lng; ++nrmarker) + stbiw__jpg_writeBits(s, bitBuf, bitCnt, M16zeroes); + nrzeroes &= 15; + } + stbiw__jpg_calcBits(DU[i], bits); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, HTAC[(nrzeroes<<4)+bits[1]]); + stbiw__jpg_writeBits(s, bitBuf, bitCnt, bits); + } + if(end0pos != 63) { + stbiw__jpg_writeBits(s, bitBuf, bitCnt, EOB); + } + return DU[0]; +} + +static int stbi_write_jpg_core(stbi__write_context *s, int width, int height, int comp, const void* data, int quality) { + // Constants that don't pollute global namespace + static const unsigned char std_dc_luminance_nrcodes[] = {0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0}; + static const unsigned char std_dc_luminance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_luminance_nrcodes[] = {0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d}; + static const unsigned char std_ac_luminance_values[] = { + 0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08, + 0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59, + 0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89, + 0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6, + 0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2, + 0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + static const unsigned char std_dc_chrominance_nrcodes[] = {0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0}; + static const unsigned char std_dc_chrominance_values[] = {0,1,2,3,4,5,6,7,8,9,10,11}; + static const unsigned char std_ac_chrominance_nrcodes[] = {0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77}; + static const unsigned char std_ac_chrominance_values[] = { + 0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91, + 0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26, + 0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58, + 0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87, + 0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4, + 0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda, + 0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,0xf9,0xfa + }; + // Huffman tables + static const unsigned short YDC_HT[256][2] = { {0,2},{2,3},{3,3},{4,3},{5,3},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9}}; + static const unsigned short UVDC_HT[256][2] = { {0,2},{1,2},{2,2},{6,3},{14,4},{30,5},{62,6},{126,7},{254,8},{510,9},{1022,10},{2046,11}}; + static const unsigned short YAC_HT[256][2] = { + {10,4},{0,2},{1,2},{4,3},{11,4},{26,5},{120,7},{248,8},{1014,10},{65410,16},{65411,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {12,4},{27,5},{121,7},{502,9},{2038,11},{65412,16},{65413,16},{65414,16},{65415,16},{65416,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {28,5},{249,8},{1015,10},{4084,12},{65417,16},{65418,16},{65419,16},{65420,16},{65421,16},{65422,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{503,9},{4085,12},{65423,16},{65424,16},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1016,10},{65430,16},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2039,11},{65438,16},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {123,7},{4086,12},{65446,16},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {250,8},{4087,12},{65454,16},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{32704,15},{65462,16},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65470,16},{65471,16},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65479,16},{65480,16},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1017,10},{65488,16},{65489,16},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{65497,16},{65498,16},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2040,11},{65506,16},{65507,16},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {65515,16},{65516,16},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65525,16},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const unsigned short UVAC_HT[256][2] = { + {0,2},{1,2},{4,3},{10,4},{24,5},{25,5},{56,6},{120,7},{500,9},{1014,10},{4084,12},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {11,4},{57,6},{246,8},{501,9},{2038,11},{4085,12},{65416,16},{65417,16},{65418,16},{65419,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {26,5},{247,8},{1015,10},{4086,12},{32706,15},{65420,16},{65421,16},{65422,16},{65423,16},{65424,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {27,5},{248,8},{1016,10},{4087,12},{65425,16},{65426,16},{65427,16},{65428,16},{65429,16},{65430,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {58,6},{502,9},{65431,16},{65432,16},{65433,16},{65434,16},{65435,16},{65436,16},{65437,16},{65438,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {59,6},{1017,10},{65439,16},{65440,16},{65441,16},{65442,16},{65443,16},{65444,16},{65445,16},{65446,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {121,7},{2039,11},{65447,16},{65448,16},{65449,16},{65450,16},{65451,16},{65452,16},{65453,16},{65454,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {122,7},{2040,11},{65455,16},{65456,16},{65457,16},{65458,16},{65459,16},{65460,16},{65461,16},{65462,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {249,8},{65463,16},{65464,16},{65465,16},{65466,16},{65467,16},{65468,16},{65469,16},{65470,16},{65471,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {503,9},{65472,16},{65473,16},{65474,16},{65475,16},{65476,16},{65477,16},{65478,16},{65479,16},{65480,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {504,9},{65481,16},{65482,16},{65483,16},{65484,16},{65485,16},{65486,16},{65487,16},{65488,16},{65489,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {505,9},{65490,16},{65491,16},{65492,16},{65493,16},{65494,16},{65495,16},{65496,16},{65497,16},{65498,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {506,9},{65499,16},{65500,16},{65501,16},{65502,16},{65503,16},{65504,16},{65505,16},{65506,16},{65507,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {2041,11},{65508,16},{65509,16},{65510,16},{65511,16},{65512,16},{65513,16},{65514,16},{65515,16},{65516,16},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}, + {16352,14},{65517,16},{65518,16},{65519,16},{65520,16},{65521,16},{65522,16},{65523,16},{65524,16},{65525,16},{0,0},{0,0},{0,0},{0,0},{0,0}, + {1018,10},{32707,15},{65526,16},{65527,16},{65528,16},{65529,16},{65530,16},{65531,16},{65532,16},{65533,16},{65534,16},{0,0},{0,0},{0,0},{0,0},{0,0} + }; + static const int YQT[] = {16,11,10,16,24,40,51,61,12,12,14,19,26,58,60,55,14,13,16,24,40,57,69,56,14,17,22,29,51,87,80,62,18,22, + 37,56,68,109,103,77,24,35,55,64,81,104,113,92,49,64,78,87,103,121,120,101,72,92,95,98,112,100,103,99}; + static const int UVQT[] = {17,18,24,47,99,99,99,99,18,21,26,66,99,99,99,99,24,26,56,99,99,99,99,99,47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99,99}; + static const float aasf[] = { 1.0f * 2.828427125f, 1.387039845f * 2.828427125f, 1.306562965f * 2.828427125f, 1.175875602f * 2.828427125f, + 1.0f * 2.828427125f, 0.785694958f * 2.828427125f, 0.541196100f * 2.828427125f, 0.275899379f * 2.828427125f }; + + int row, col, i, k; + float fdtbl_Y[64], fdtbl_UV[64]; + unsigned char YTable[64], UVTable[64]; + + if(!data || !width || !height || comp > 4 || comp < 1) { + return 0; + } + + quality = quality ? quality : 90; + quality = quality < 1 ? 1 : quality > 100 ? 100 : quality; + quality = quality < 50 ? 5000 / quality : 200 - quality * 2; + + for(i = 0; i < 64; ++i) { + int uvti, yti = (YQT[i]*quality+50)/100; + YTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (yti < 1 ? 1 : yti > 255 ? 255 : yti); + uvti = (UVQT[i]*quality+50)/100; + UVTable[stbiw__jpg_ZigZag[i]] = (unsigned char) (uvti < 1 ? 1 : uvti > 255 ? 255 : uvti); + } + + for(row = 0, k = 0; row < 8; ++row) { + for(col = 0; col < 8; ++col, ++k) { + fdtbl_Y[k] = 1 / (YTable [stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + fdtbl_UV[k] = 1 / (UVTable[stbiw__jpg_ZigZag[k]] * aasf[row] * aasf[col]); + } + } + + // Write Headers + { + static const unsigned char head0[] = { 0xFF,0xD8,0xFF,0xE0,0,0x10,'J','F','I','F',0,1,1,0,0,1,0,1,0,0,0xFF,0xDB,0,0x84,0 }; + static const unsigned char head2[] = { 0xFF,0xDA,0,0xC,3,1,0,2,0x11,3,0x11,0,0x3F,0 }; + const unsigned char head1[] = { 0xFF,0xC0,0,0x11,8,(unsigned char)(height>>8),STBIW_UCHAR(height),(unsigned char)(width>>8),STBIW_UCHAR(width), + 3,1,0x11,0,2,0x11,1,3,0x11,1,0xFF,0xC4,0x01,0xA2,0 }; + s->func(s->context, (void*)head0, sizeof(head0)); + s->func(s->context, (void*)YTable, sizeof(YTable)); + stbiw__putc(s, 1); + s->func(s->context, UVTable, sizeof(UVTable)); + s->func(s->context, (void*)head1, sizeof(head1)); + s->func(s->context, (void*)(std_dc_luminance_nrcodes+1), sizeof(std_dc_luminance_nrcodes)-1); + s->func(s->context, (void*)std_dc_luminance_values, sizeof(std_dc_luminance_values)); + stbiw__putc(s, 0x10); // HTYACinfo + s->func(s->context, (void*)(std_ac_luminance_nrcodes+1), sizeof(std_ac_luminance_nrcodes)-1); + s->func(s->context, (void*)std_ac_luminance_values, sizeof(std_ac_luminance_values)); + stbiw__putc(s, 1); // HTUDCinfo + s->func(s->context, (void*)(std_dc_chrominance_nrcodes+1), sizeof(std_dc_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_dc_chrominance_values, sizeof(std_dc_chrominance_values)); + stbiw__putc(s, 0x11); // HTUACinfo + s->func(s->context, (void*)(std_ac_chrominance_nrcodes+1), sizeof(std_ac_chrominance_nrcodes)-1); + s->func(s->context, (void*)std_ac_chrominance_values, sizeof(std_ac_chrominance_values)); + s->func(s->context, (void*)head2, sizeof(head2)); + } + + // Encode 8x8 macroblocks + { + static const unsigned short fillBits[] = {0x7F, 7}; + const unsigned char *imageData = (const unsigned char *)data; + int DCY=0, DCU=0, DCV=0; + int bitBuf=0, bitCnt=0; + // comp == 2 is grey+alpha (alpha is ignored) + int ofsG = comp > 2 ? 1 : 0, ofsB = comp > 2 ? 2 : 0; + int x, y, pos; + for(y = 0; y < height; y += 8) { + for(x = 0; x < width; x += 8) { + float YDU[64], UDU[64], VDU[64]; + for(row = y, pos = 0; row < y+8; ++row) { + // row >= height => use last input row + int clamped_row = (row < height) ? row : height - 1; + int base_p = (stbi__flip_vertically_on_write ? (height-1-clamped_row) : clamped_row)*width*comp; + for(col = x; col < x+8; ++col, ++pos) { + float r, g, b; + // if col >= width => use pixel from last input column + int p = base_p + ((col < width) ? col : (width-1))*comp; + + r = imageData[p+0]; + g = imageData[p+ofsG]; + b = imageData[p+ofsB]; + YDU[pos]=+0.29900f*r+0.58700f*g+0.11400f*b-128; + UDU[pos]=-0.16874f*r-0.33126f*g+0.50000f*b; + VDU[pos]=+0.50000f*r-0.41869f*g-0.08131f*b; + } + } + + DCY = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, YDU, fdtbl_Y, DCY, YDC_HT, YAC_HT); + DCU = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, UDU, fdtbl_UV, DCU, UVDC_HT, UVAC_HT); + DCV = stbiw__jpg_processDU(s, &bitBuf, &bitCnt, VDU, fdtbl_UV, DCV, UVDC_HT, UVAC_HT); + } + } + + // Do the bit alignment of the EOI marker + stbiw__jpg_writeBits(s, &bitBuf, &bitCnt, fillBits); + } + + // EOI + stbiw__putc(s, 0xFF); + stbiw__putc(s, 0xD9); + + return 1; +} + +STBIWDEF int stbi_write_jpg_to_func(stbi_write_func *func, void *context, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + stbi__start_write_callbacks(&s, func, context); + return stbi_write_jpg_core(&s, x, y, comp, (void *) data, quality); +} + + +#ifndef STBI_WRITE_NO_STDIO +STBIWDEF int stbi_write_jpg(char const *filename, int x, int y, int comp, const void *data, int quality) +{ + stbi__write_context s; + if (stbi__start_write_file(&s,filename)) { + int r = stbi_write_jpg_core(&s, x, y, comp, data, quality); + stbi__end_write_file(&s); + return r; + } else + return 0; +} +#endif + +#endif // STB_IMAGE_WRITE_IMPLEMENTATION + +/* Revision history + 1.11 (2019-08-11) + + 1.10 (2019-02-07) + support utf8 filenames in Windows; fix warnings and platform ifdefs + 1.09 (2018-02-11) + fix typo in zlib quality API, improve STB_I_W_STATIC in C++ + 1.08 (2018-01-29) + add stbi__flip_vertically_on_write, external zlib, zlib quality, choose PNG filter + 1.07 (2017-07-24) + doc fix + 1.06 (2017-07-23) + writing JPEG (using Jon Olick's code) + 1.05 ??? + 1.04 (2017-03-03) + monochrome BMP expansion + 1.03 ??? + 1.02 (2016-04-02) + avoid allocating large structures on the stack + 1.01 (2016-01-16) + STBIW_REALLOC_SIZED: support allocators with no realloc support + avoid race-condition in crc initialization + minor compile issues + 1.00 (2015-09-14) + installable file IO function + 0.99 (2015-09-13) + warning fixes; TGA rle support + 0.98 (2015-04-08) + added STBIW_MALLOC, STBIW_ASSERT etc + 0.97 (2015-01-18) + fixed HDR asserts, rewrote HDR rle logic + 0.96 (2015-01-17) + add HDR output + fix monochrome BMP + 0.95 (2014-08-17) + add monochrome TGA output + 0.94 (2014-05-31) + rename private functions to avoid conflicts with stb_image.h + 0.93 (2014-05-27) + warning fixes + 0.92 (2010-08-01) + casts to unsigned char to fix warnings + 0.91 (2010-07-17) + first public release + 0.90 first internal release +*/ + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/PortabilityLayer/CFileStream.cpp b/PortabilityLayer/CFileStream.cpp index 700a041..11cc4f5 100644 --- a/PortabilityLayer/CFileStream.cpp +++ b/PortabilityLayer/CFileStream.cpp @@ -101,7 +101,7 @@ namespace PortabilityLayer return 0; long oldPos = ftell(m_file); - fseek(m_file, SEEK_END, 0); + fseek(m_file, 0, SEEK_END); const UFilePos_t endPos = static_cast(ftell(m_file)); fseek(m_file, oldPos, SEEK_SET); diff --git a/PortabilityLayer/ClientAudioChannelContext.h b/PortabilityLayer/ClientAudioChannelContext.h new file mode 100644 index 0000000..22d39fe --- /dev/null +++ b/PortabilityLayer/ClientAudioChannelContext.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + class ClientAudioChannelContext + { + public: + virtual void NotifyBufferFinished() = 0; + }; +} diff --git a/PortabilityLayer/DisplayDeviceManager.cpp b/PortabilityLayer/DisplayDeviceManager.cpp index a0e0dd5..ac3b745 100644 --- a/PortabilityLayer/DisplayDeviceManager.cpp +++ b/PortabilityLayer/DisplayDeviceManager.cpp @@ -1,7 +1,7 @@ #include "DisplayDeviceManager.h" - + +#include "HostDisplayDriver.h" #include "PLQuickdraw.h" - #include "MemoryManager.h" namespace PortabilityLayer @@ -37,7 +37,9 @@ namespace PortabilityLayer void DisplayDeviceManagerImpl::Init() { - m_mainDevice = MemoryManager::GetInstance()->NewHandle(); + m_mainDevice = MemoryManager::GetInstance()->NewHandle(); + + HostDisplayDriver::GetInstance()->GetDisplayResolution(nullptr, nullptr, &(*m_mainDevice)->pixelFormat); } void DisplayDeviceManagerImpl::Shutdown() diff --git a/PortabilityLayer/GpAppInterface.h b/PortabilityLayer/GpAppInterface.h index 0d1f844..85d2086 100644 --- a/PortabilityLayer/GpAppInterface.h +++ b/PortabilityLayer/GpAppInterface.h @@ -1,42 +1,44 @@ -#pragma once -#ifndef __GPAPP_INTERFACE_H__ -#define __GPAPP_INTERFACE_H__ - -#include "HostSuspendHook.h" -#include - -#ifdef GP_APP_DLL - -#ifdef GP_APP_DLL_EXPORT -#define GP_APP_DLL_EXPORT_API extern "C" __declspec(dllexport) -#else -#define GP_APP_DLL_EXPORT_API extern "C" __declspec(dllimport) -#endif - -#else - -#define GP_APP_DLL_EXPORT_API extern "C" - -#endif - -namespace PortabilityLayer -{ - class HostFileSystem; - class HostDisplayDriver; - class HostSystemServices; -} - -class GpAppInterface -{ -public: - virtual int ApplicationMain() = 0; - virtual void PL_IncrementTickCounter(uint32_t count) = 0; - virtual void PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) = 0; - virtual void PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) = 0; - virtual void PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) = 0; - virtual void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) = 0; -}; - -GP_APP_DLL_EXPORT_API GpAppInterface *GpAppInterface_Get(); - -#endif +#pragma once +#ifndef __GPAPP_INTERFACE_H__ +#define __GPAPP_INTERFACE_H__ + +#include "HostSuspendHook.h" +#include + +#ifdef GP_APP_DLL + +#ifdef GP_APP_DLL_EXPORT +#define GP_APP_DLL_EXPORT_API extern "C" __declspec(dllexport) +#else +#define GP_APP_DLL_EXPORT_API extern "C" __declspec(dllimport) +#endif + +#else + +#define GP_APP_DLL_EXPORT_API extern "C" + +#endif + +namespace PortabilityLayer +{ + class HostAudioDriver; + class HostFileSystem; + class HostDisplayDriver; + class HostSystemServices; +} + +class GpAppInterface +{ +public: + virtual int ApplicationMain() = 0; + virtual void PL_IncrementTickCounter(uint32_t count) = 0; + virtual void PL_HostFileSystem_SetInstance(PortabilityLayer::HostFileSystem *instance) = 0; + virtual void PL_HostDisplayDriver_SetInstance(PortabilityLayer::HostDisplayDriver *instance) = 0; + virtual void PL_HostSystemServices_SetInstance(PortabilityLayer::HostSystemServices *instance) = 0; + virtual void PL_HostAudioDriver_SetInstance(PortabilityLayer::HostAudioDriver *instance) = 0; + virtual void PL_InstallHostSuspendHook(PortabilityLayer::HostSuspendHook_t hook, void *context) = 0; +}; + +GP_APP_DLL_EXPORT_API GpAppInterface *GpAppInterface_Get(); + +#endif diff --git a/PortabilityLayer/HostAudioChannel.h b/PortabilityLayer/HostAudioChannel.h new file mode 100644 index 0000000..303263e --- /dev/null +++ b/PortabilityLayer/HostAudioChannel.h @@ -0,0 +1,14 @@ +#pragma once + +namespace PortabilityLayer +{ + class ClientAudioChannelContext; + + class HostAudioChannel + { + public: + virtual void SetClientAudioChannelContext(ClientAudioChannelContext *context) = 0; + virtual void PostBuffer(const void *buffer, size_t bufferSize) = 0; + virtual void Destroy() = 0; + }; +} diff --git a/PortabilityLayer/HostAudioDriver.cpp b/PortabilityLayer/HostAudioDriver.cpp new file mode 100644 index 0000000..fb95518 --- /dev/null +++ b/PortabilityLayer/HostAudioDriver.cpp @@ -0,0 +1,16 @@ +#include "HostAudioDriver.h" + +namespace PortabilityLayer +{ + HostAudioDriver *HostAudioDriver::GetInstance() + { + return ms_instance; + } + + void HostAudioDriver::SetInstance(HostAudioDriver *instance) + { + ms_instance = instance; + } + + HostAudioDriver *HostAudioDriver::ms_instance; +} diff --git a/PortabilityLayer/HostAudioDriver.h b/PortabilityLayer/HostAudioDriver.h new file mode 100644 index 0000000..936710a --- /dev/null +++ b/PortabilityLayer/HostAudioDriver.h @@ -0,0 +1,18 @@ +#pragma once + +namespace PortabilityLayer +{ + class HostAudioChannel; + + class HostAudioDriver + { + public: + virtual HostAudioChannel *CreateChannel() = 0; + + static HostAudioDriver *GetInstance(); + static void SetInstance(HostAudioDriver *instance); + + private: + static HostAudioDriver *ms_instance; + }; +} diff --git a/PortabilityLayer/HostDisplayDriver.h b/PortabilityLayer/HostDisplayDriver.h index 35e1931..5df4060 100644 --- a/PortabilityLayer/HostDisplayDriver.h +++ b/PortabilityLayer/HostDisplayDriver.h @@ -1,21 +1,23 @@ -#pragma once -#ifndef __PL_HOST_DISPLAY_DRIVER_H__ -#define __PL_HOST_DISPLAY_DRIVER_H__ - -namespace PortabilityLayer -{ - class HostDisplayDriver - { - public: - virtual void GetDisplayResolution(unsigned int &width, unsigned int &height) = 0; - virtual void HideCursor() = 0; - - static void SetInstance(HostDisplayDriver *instance); - static HostDisplayDriver *GetInstance(); - - private: - static HostDisplayDriver *ms_instance; - }; -} - -#endif +#pragma once +#ifndef __PL_HOST_DISPLAY_DRIVER_H__ +#define __PL_HOST_DISPLAY_DRIVER_H__ + +#include "PixelFormat.h" + +namespace PortabilityLayer +{ + class HostDisplayDriver + { + public: + virtual void GetDisplayResolution(unsigned int *width, unsigned int *height, PixelFormat *pixelFormat) = 0; + virtual void HideCursor() = 0; + + static void SetInstance(HostDisplayDriver *instance); + static HostDisplayDriver *GetInstance(); + + private: + static HostDisplayDriver *ms_instance; + }; +} + +#endif diff --git a/PortabilityLayer/HostMutex.h b/PortabilityLayer/HostMutex.h new file mode 100644 index 0000000..ad4c050 --- /dev/null +++ b/PortabilityLayer/HostMutex.h @@ -0,0 +1,13 @@ +#pragma once + +namespace PortabilityLayer +{ + class HostMutex + { + public: + virtual void Destroy() = 0; + + virtual void Lock() = 0; + virtual void Unlock() = 0; + }; +} diff --git a/PortabilityLayer/HostSystemServices.h b/PortabilityLayer/HostSystemServices.h index a1287a1..a2eae42 100644 --- a/PortabilityLayer/HostSystemServices.h +++ b/PortabilityLayer/HostSystemServices.h @@ -2,14 +2,23 @@ #ifndef __PL_HOST_SYSTEM_SERVICES_H__ #define __PL_HOST_SYSTEM_SERVICES_H__ -#include +#include + +#ifdef CreateMutex +#error "CreateMutex was macrod" +#endif namespace PortabilityLayer -{ +{ + class HostMutex; + class HostThreadEvent; + class HostSystemServices { public: - virtual uint32_t GetTime() const = 0; + virtual uint32_t GetTime() const = 0; + virtual HostMutex *CreateMutex() = 0; + virtual HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) = 0; static void SetInstance(HostSystemServices *instance); static HostSystemServices *GetInstance(); diff --git a/PortabilityLayer/HostThreadEvent.h b/PortabilityLayer/HostThreadEvent.h new file mode 100644 index 0000000..7f28481 --- /dev/null +++ b/PortabilityLayer/HostThreadEvent.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + class HostThreadEvent + { + public: + virtual void Wait() = 0; + virtual void WaitTimed(uint32_t msec) = 0; + virtual void Signal() = 0; + virtual void Destroy() = 0; + }; +} diff --git a/PortabilityLayer/MemoryManager.cpp b/PortabilityLayer/MemoryManager.cpp index 8d11e16..7b7c075 100644 --- a/PortabilityLayer/MemoryManager.cpp +++ b/PortabilityLayer/MemoryManager.cpp @@ -5,6 +5,8 @@ #include #include +#include +#include namespace PortabilityLayer { @@ -13,16 +15,19 @@ namespace PortabilityLayer public: void Init() override; void Shutdown() override; - - void *Alloc(size_t size) override; - void Release(void *buf) override; + + void *Alloc(size_t size) override; + void *Realloc(void *buf, size_t newSize) override; + void Release(void *buf) override; MMHandleBlock *AllocHandle(size_t size) override; + bool ResizeHandle(MMHandleBlock *hdl, size_t newSize) override; void ReleaseHandle(MMHandleBlock *hdl) override; static MemoryManagerImpl *GetInstance(); private: + static MemoryManagerImpl ms_instance; }; @@ -32,6 +37,41 @@ namespace PortabilityLayer void MemoryManagerImpl::Shutdown() { + } + + void *MemoryManagerImpl::Realloc(void *buf, size_t newSize) + { + assert(buf != nullptr); + + const size_t mmBlockSize = MMBlock::AlignedSize(); + uint8_t *oldBufBytes = static_cast(buf); + const MMBlock *oldBufMMBlock = reinterpret_cast(oldBufBytes - MMBlock::AlignedSize()); + + const size_t oldBufOffsetFromAlignLoc = oldBufMMBlock->m_offsetFromAllocLocation; + uint8_t *oldBufBase = oldBufBytes - MMBlock::AlignedSize() - oldBufOffsetFromAlignLoc; + + const size_t mmBlockSizeWithMaxPadding = MMBlock::AlignedSize() + PL_SYSTEM_MEMORY_ALIGNMENT - 1; + if (SIZE_MAX - newSize < mmBlockSizeWithMaxPadding) + return nullptr; + + const size_t newBufferSize = newSize + mmBlockSizeWithMaxPadding; + uint8_t *newBuffer = static_cast(realloc(oldBufBase, newSize + mmBlockSizeWithMaxPadding)); + if (!newBuffer) + return nullptr; + + const intptr_t offsetFromAlignPoint = reinterpret_cast(newBuffer) & static_cast(PL_SYSTEM_MEMORY_ALIGNMENT - 1); + intptr_t alignPadding = 0; + if (offsetFromAlignPoint != 0) + alignPadding = static_cast(PL_SYSTEM_MEMORY_ALIGNMENT) - offsetFromAlignPoint; + + // Check if the alignment changed, if so relocate + if (static_cast(alignPadding) != oldBufOffsetFromAlignLoc) + memmove(newBuffer + alignPadding, newBuffer + oldBufOffsetFromAlignLoc, MMBlock::AlignedSize() + newSize); + + MMBlock *newMMBlock = reinterpret_cast(newBuffer + alignPadding); + newMMBlock->m_offsetFromAllocLocation = static_cast::ValueType_t>(alignPadding); + + return newBuffer + alignPadding + MMBlock::AlignedSize(); } void *MemoryManagerImpl::Alloc(size_t size) @@ -77,6 +117,24 @@ namespace PortabilityLayer MMHandleBlock *handleBlock = static_cast(Alloc(sizeof(MMHandleBlock))); return new (handleBlock) MMHandleBlock(contents, size); + } + + bool MemoryManagerImpl::ResizeHandle(MMHandleBlock *hdl, size_t newSize) + { + if (hdl->m_contents == nullptr) + return false; + + if (newSize != hdl->m_size) + { + void *newBuf = Realloc(hdl->m_contents, newSize); + if (!newBuf) + return false; + + hdl->m_contents = newBuf; + hdl->m_size = newSize; + } + + return true; } void MemoryManagerImpl::ReleaseHandle(MMHandleBlock *hdl) @@ -84,8 +142,10 @@ namespace PortabilityLayer if (!hdl) return; - if (hdl->m_rmSelfRef != nullptr) - hdl->m_rmSelfRef->m_handle = nullptr; + assert(hdl->m_rmSelfRef == nullptr); + + if (hdl->m_contents) + Release(hdl->m_contents); hdl->~MMHandleBlock(); diff --git a/PortabilityLayer/MemoryManager.h b/PortabilityLayer/MemoryManager.h index 7a9de31..2ed0a66 100644 --- a/PortabilityLayer/MemoryManager.h +++ b/PortabilityLayer/MemoryManager.h @@ -1,51 +1,53 @@ -#pragma once -#ifndef __PL_MEMORY_MANAGER_H__ -#define __PL_MEMORY_MANAGER_H__ - -#include - -namespace PortabilityLayer -{ - struct MMHandleBlock; - - class MemoryManager - { - public: - virtual void Init() = 0; - virtual void Shutdown() = 0; - - virtual void *Alloc(size_t size) = 0; - virtual void Release(void *buf) = 0; - - virtual MMHandleBlock *AllocHandle(size_t size) = 0; - virtual void ReleaseHandle(MMHandleBlock *hdl) = 0; - - template - T **NewHandle(); - - static MemoryManager *GetInstance(); - }; -} - -#include -#include "CoreDefs.h" -#include "MMHandleBlock.h" - -namespace PortabilityLayer -{ - template - T **MemoryManager::NewHandle() - { - MMHandleBlock *hdl = this->AllocHandle(sizeof(T)); - if (!hdl) - return nullptr; - - T **objectHdl = reinterpret_cast(hdl); - T *objectPtr = *objectHdl; - new (objectPtr) T(); - - return objectHdl; - } -} - -#endif +#pragma once +#ifndef __PL_MEMORY_MANAGER_H__ +#define __PL_MEMORY_MANAGER_H__ + +#include + +namespace PortabilityLayer +{ + struct MMHandleBlock; + + class MemoryManager + { + public: + virtual void Init() = 0; + virtual void Shutdown() = 0; + + virtual void *Alloc(size_t size) = 0; + virtual void Release(void *buf) = 0; + virtual void *Realloc(void *buf, size_t newSize) = 0; + + virtual MMHandleBlock *AllocHandle(size_t size) = 0; + virtual bool ResizeHandle(MMHandleBlock *hdl, size_t newSize) = 0; + virtual void ReleaseHandle(MMHandleBlock *hdl) = 0; + + template + T **NewHandle(); + + static MemoryManager *GetInstance(); + }; +} + +#include +#include "CoreDefs.h" +#include "MMHandleBlock.h" + +namespace PortabilityLayer +{ + template + T **MemoryManager::NewHandle() + { + MMHandleBlock *hdl = this->AllocHandle(sizeof(T)); + if (!hdl) + return nullptr; + + T **objectHdl = reinterpret_cast(hdl); + T *objectPtr = *objectHdl; + new (objectPtr) T(); + + return objectHdl; + } +} + +#endif diff --git a/PortabilityLayer/PLBigEndian.h b/PortabilityLayer/PLBigEndian.h index a70d2bc..c7a3a39 100644 --- a/PortabilityLayer/PLBigEndian.h +++ b/PortabilityLayer/PLBigEndian.h @@ -37,7 +37,7 @@ public: BEInteger operator++(int); private: - T m_beValue; + uint8_t m_beValueBytes[sizeof(T)]; }; template @@ -83,30 +83,32 @@ struct BEInteger_SwapHelper } }; -// Int16 +#include + template inline BEInteger::BEInteger() - : m_beValue(0) { + memset(m_beValueBytes, 0, sizeof(T)); } template inline BEInteger::BEInteger(const BEInteger &other) - : m_beValue(other.m_beValue) { + memcpy(m_beValueBytes, other.m_beValueBytes, sizeof(T)); } template inline BEInteger::BEInteger(T i) - : m_beValue(i) { - BEInteger_SwapHelper::Swap(m_beValue); + BEInteger_SwapHelper::Swap(i); + memcpy(m_beValueBytes, &i, sizeof(T)); } template inline BEInteger::operator T() const { - int16_t result = m_beValue; + T result; + memcpy(&result, m_beValueBytes, sizeof(T)); BEInteger_SwapHelper::Swap(result); return result; } @@ -115,7 +117,7 @@ template inline BEInteger &BEInteger::operator=(T value) { BEInteger_SwapHelper::Swap(value); - m_beValue = value; + memcpy(m_beValueBytes, &value, sizeof(T)); return *this; } @@ -123,9 +125,14 @@ template template BEInteger &BEInteger::operator+=(TOther value) { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue += value; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue += value; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -133,9 +140,14 @@ template template BEInteger &BEInteger::operator-=(TOther value) { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue -= value; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue -= value; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -143,9 +155,14 @@ template template BEInteger &BEInteger::operator*=(TOther value) { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue *= value; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue *= value; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -153,9 +170,14 @@ template template BEInteger &BEInteger::operator/=(TOther value) { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue /= value; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue /= value; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -163,18 +185,28 @@ template template BEInteger &BEInteger::operator%=(TOther value) { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue %= value; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue %= value; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } template BEInteger& BEInteger::operator--() { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue--; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue--; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -189,9 +221,14 @@ BEInteger BEInteger::operator--(int) template BEInteger& BEInteger::operator++() { - BEInteger_SwapHelper::Swap(m_beValue); - m_beValue++; - BEInteger_SwapHelper::Swap(m_beValue); + T storedValue; + memcpy(&storedValue, m_beValueBytes, sizeof(T)); + + BEInteger_SwapHelper::Swap(storedValue); + storedValue++; + BEInteger_SwapHelper::Swap(storedValue); + + memcpy(m_beValueBytes, &storedValue, sizeof(T)); return *this; } @@ -209,4 +246,10 @@ typedef BEInteger BEInt32_t; typedef BEInteger BEUInt16_t; typedef BEInteger BEUInt32_t; +struct BEFixed32_t +{ + BEInt16_t m_intPart; + BEUInt16_t m_fracPart; +}; + #endif diff --git a/PortabilityLayer/PLCore.cpp b/PortabilityLayer/PLCore.cpp index 702a3fd..b103cf4 100644 --- a/PortabilityLayer/PLCore.cpp +++ b/PortabilityLayer/PLCore.cpp @@ -12,9 +12,14 @@ #include "HostSystemServices.h" #include "ResourceManager.h" #include "MemoryManager.h" +#include "MemReaderStream.h" #include "MMHandleBlock.h" #include "ResTypeID.h" #include "RandomNumberGenerator.h" +#include "PLBigEndian.h" +#include "QDManager.h" +#include "WindowDef.h" +#include "WindowManager.h" #include @@ -22,6 +27,17 @@ void InitCursor() { } +Rect BERect::ToRect() const +{ + Rect rect; + rect.top = this->top; + rect.bottom = this->bottom; + rect.left = this->left; + rect.right = this->right; + + return rect; +} + OSErr FSClose(short fsRef) { PL_NotYetImplemented(); @@ -62,6 +78,11 @@ void SetCursor(CursPtr cursor) PL_NotYetImplemented(); } +void SetBuiltinCursor(int builtinCursor) +{ + PL_NotYetImplemented(); +} + void DisposeCCursor(CCrsrHandle handle) { PL_NotYetImplemented(); @@ -168,8 +189,24 @@ void GetWindowBounds(WindowPtr window, WindowRegionType windowRegion, Rect *rect WindowPtr GetNewCWindow(int resID, void *storage, WindowPtr behind) { - PL_NotYetImplemented(); - return nullptr; + Handle windowResource = GetResource('WIND', resID); + + if (!windowResource) + return nullptr; + + long resSize = GetHandleSize(windowResource); + + PortabilityLayer::MemReaderStream stream(*windowResource, resSize); + + PortabilityLayer::WindowDef def; + if (!def.Deserialize(&stream)) + return nullptr; + + WindowPtr window = PortabilityLayer::WindowManager::GetInstance()->CreateWindow(def); + if (window) + PortabilityLayer::WindowManager::GetInstance()->PutWindowBehind(window, behind); + + return window; } WindowPtr NewCWindow(void *storage, const Rect *bounds, const PLPasStr &title, Boolean visible, int wdef, WindowPtr behind, Boolean hasCloseBox, long userdata) @@ -186,17 +223,21 @@ WindowPtr NewWindow(void *storage, const Rect *bounds, const PLPasStr &title, Bo void SizeWindow(WindowPtr window, int width, int height, Boolean addToUpdateRegion) { - PL_NotYetImplemented(); + PortabilityLayer::WindowManager::GetInstance()->ResizeWindow(window, width, height); } void MoveWindow(WindowPtr window, int x, int y, Boolean moveToFront) { - PL_NotYetImplemented(); + PortabilityLayer::WindowManager *windowManager = PortabilityLayer::WindowManager::GetInstance(); + + windowManager->MoveWindow(window, x, y); + if (moveToFront != 0) + windowManager->PutWindowBehind(window, windowManager->GetPutInFrontSentinel()); } void ShowWindow(WindowPtr window) { - PL_NotYetImplemented(); + PortabilityLayer::WindowManager::GetInstance()->ShowWindow(window); } void SetWTitle(WindowPtr window, const PLPasStr &title) @@ -286,7 +327,27 @@ OSErr FindFolder(int refNum, int posType, bool createFolder, short *volumeRef, l void GetIndString(unsigned char *str, int stringsID, int fnameIndex) { - PL_NotYetImplemented(); + if (fnameIndex < 1) + return; + + PortabilityLayer::MMHandleBlock *istrRes = PortabilityLayer::ResourceManager::GetInstance()->GetResource('STR#', stringsID); + if (istrRes && istrRes->m_contents) + { + const uint8_t *contentsBytes = static_cast(istrRes->m_contents); + const BEUInt16_t *pArraySize = reinterpret_cast(contentsBytes); + + const uint16_t arraySize = *pArraySize; + + if (fnameIndex > static_cast(arraySize)) + return; + + const uint8_t *strStart = contentsBytes + 2; + for (int i = 1; i < fnameIndex; i++) + strStart += (*strStart) + 1; + + str[0] = strStart[0]; + memcpy(str + 1, strStart + 1, *strStart); + } } OSErr PBDirCreate(HFileParam *fileParam, bool asynchronous) @@ -489,13 +550,16 @@ Handle NewHandle(Size size) void DisposeHandle(Handle handle) { - PL_NotYetImplemented(); + PortabilityLayer::MemoryManager::GetInstance()->ReleaseHandle(reinterpret_cast(handle)); } long GetHandleSize(Handle handle) { - PL_NotYetImplemented(); - return 0; + if (!handle) + return 0; + + PortabilityLayer::MMHandleBlock *block = reinterpret_cast(handle); + return static_cast(block->m_size); } void HNoPurge(Handle hdl) @@ -527,14 +591,16 @@ void SetHandleSize(Handle hdl, Size newSize) void *NewPtr(Size size) { - PL_NotYetImplemented(); - return nullptr; + return PortabilityLayer::MemoryManager::GetInstance()->Alloc(size); } void *NewPtrClear(Size size) { - PL_NotYetImplemented(); - return nullptr; + void *data = NewPtr(size); + if (data != nullptr && size != 0) + memset(data, 0, size); + + return data; } void DisposePtr(void *ptr) @@ -572,7 +638,7 @@ OSErr MemError() void BlockMove(const void *src, void *dest, Size size) { - PL_NotYetImplemented(); + memcpy(dest, src, size); } Boolean WaitNextEvent(int eventMask, EventRecord *eventOut, long sleep, void *unknown) @@ -611,4 +677,15 @@ void PL_Init() PortabilityLayer::ResourceManager::GetInstance()->Init(); PortabilityLayer::DisplayDeviceManager::GetInstance()->Init(); PortabilityLayer::AEManager::GetInstance()->Init(); + PortabilityLayer::QDManager::GetInstance()->Init(); +} + +WindowPtr PL_GetPutInFrontWindowPtr() +{ + return PortabilityLayer::WindowManager::GetInstance()->GetPutInFrontSentinel(); +} + +Window::Window() + : m_port(PortabilityLayer::QDPortType_Window) +{ } diff --git a/PortabilityLayer/PLCore.h b/PortabilityLayer/PLCore.h index d15e086..4b9d649 100644 --- a/PortabilityLayer/PLCore.h +++ b/PortabilityLayer/PLCore.h @@ -4,6 +4,8 @@ #include "DataTypes.h" #include "PLErrorCodes.h" +#include "SharedTypes.h" +#include "QDPort.h" #ifdef _MSC_VER #pragma warning(error:4311) // Pointer truncation to int @@ -33,24 +35,11 @@ typedef unsigned char Str255[256]; typedef unsigned char *StringPtr; class PLPasStr; +struct CGraf; +struct Region; #define PL_DEAD(n) ((void)0) -typedef struct Point -{ - short v; - short h; -} Point; - -// FIXME: Audit -struct Rect -{ - short top; - short left; - short bottom; - short right; -}; - struct FinderInfoBlock { Int32 fdType; @@ -93,21 +82,18 @@ struct CCursor { }; -struct CGraf -{ -}; struct Window { + Window(); + + PortabilityLayer::QDPort m_port; // Must be the first item }; struct Menu { }; -struct Region -{ -}; struct DateTimeRec { @@ -280,6 +266,7 @@ void SetCCursor(CCrsrHandle handle); void HideCursor(); void SetCursor(CursPtr cursor); +void SetBuiltinCursor(int builtinCursor); void DisposeCCursor(CCrsrHandle handle); @@ -402,6 +389,7 @@ void DebugStr(const PLPasStr &str); static const Boolean PL_TRUE = 1; static const Boolean PL_FALSE = 0; +WindowPtr PL_GetPutInFrontWindowPtr(); void PL_NotYetImplemented(); void PL_NotYetImplemented_Minor(); diff --git a/PortabilityLayer/PLDialogs.h b/PortabilityLayer/PLDialogs.h index b4805ad..0716cda 100644 --- a/PortabilityLayer/PLDialogs.h +++ b/PortabilityLayer/PLDialogs.h @@ -6,7 +6,7 @@ class PLPasStr; -struct Dialog +struct Dialog : public PortabilityLayer::QDPort { }; diff --git a/PortabilityLayer/PLErrorCodes.h b/PortabilityLayer/PLErrorCodes.h index abc5d58..acd18b2 100644 --- a/PortabilityLayer/PLErrorCodes.h +++ b/PortabilityLayer/PLErrorCodes.h @@ -1,6 +1,6 @@ -#pragma once -#ifndef __PL_ERROR_CODES_H__ -#define __PL_ERROR_CODES_H__ +#pragma once +#ifndef __PL_ERROR_CODES_H__ +#define __PL_ERROR_CODES_H__ enum ErrorCodes { @@ -25,6 +25,7 @@ enum ErrorCodes volOffLinErr, permErr, wrPermErr, + queueFull, genericErr, }; diff --git a/PortabilityLayer/PLMovies.cpp b/PortabilityLayer/PLMovies.cpp index 410532a..499148f 100644 --- a/PortabilityLayer/PLMovies.cpp +++ b/PortabilityLayer/PLMovies.cpp @@ -1,11 +1,10 @@ -#include "PLMovies.h" - -OSErr EnterMovies() -{ - PL_NotYetImplemented(); - return noErr; -} - +#include "PLMovies.h" + +OSErr EnterMovies() +{ + return noErr; +} + UserData GetMovieUserData(Movie movie) { PL_NotYetImplemented(); @@ -137,5 +136,5 @@ void SetMovieBox(Movie movie, const Rect *rect) void SetMovieDisplayClipRgn(Movie movie, RgnHandle region) { - PL_NotYetImplemented(); -} + PL_NotYetImplemented(); +} diff --git a/PortabilityLayer/PLQDOffscreen.cpp b/PortabilityLayer/PLQDOffscreen.cpp index d49108c..678885e 100644 --- a/PortabilityLayer/PLQDOffscreen.cpp +++ b/PortabilityLayer/PLQDOffscreen.cpp @@ -1,10 +1,240 @@ -#include "PLQDOffscreen.h" - +#include "PLQDOffscreen.h" +#include "MemoryManager.h" +#include "MemReaderStream.h" +#include "ResourceManager.h" +#include "Rect2i.h" +#include "ResTypeID.h" +#include "QDStandardPalette.h" +#include "QDManager.h" +#include "QDGraf.h" +#include "QDPictDecoder.h" +#include "QDPictEmitContext.h" +#include "QDPictEmitScanlineParameters.h" +#include "QDPictHeader.h" +#include "QDPictOpcodes.h" +#include "QDPixMap.h" +#include "Vec2i.h" + +#include +#include + +namespace PortabilityLayer +{ + class PixMapBlitEmitter final : public QDPictEmitContext + { + public: + PixMapBlitEmitter(const Vec2i &drawOrigin, PixMapImpl *pixMap); + ~PixMapBlitEmitter(); + + bool SpecifyFrame(const Rect &rect) override; + Rect ConstrainRegion(const Rect &rect) const override; + void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) override; + void BlitScanlineAndAdvance(const void *) override; + bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) override; + + private: + PixMapImpl *m_pixMap; + Vec2i m_drawOrigin; + uint8_t *m_tempBuffer; + Rect m_specFrame; + + QDPictBlitSourceType m_blitType; + QDPictEmitScanlineParameters m_params; + + size_t m_constraintRegionWidth; + size_t m_constraintRegionStartIndex; + size_t m_constraintRegionEndIndex; + size_t m_outputIndexStart; + + uint8_t m_paletteMap[256]; + bool m_sourceAndDestPalettesAreSame; + }; + + PixMapBlitEmitter::PixMapBlitEmitter(const Vec2i &drawOrigin, PixMapImpl *pixMap) + : m_pixMap(pixMap) + , m_drawOrigin(drawOrigin) + , m_tempBuffer(nullptr) + , m_sourceAndDestPalettesAreSame(false) + { + } + + PixMapBlitEmitter::~PixMapBlitEmitter() + { + if (m_tempBuffer) + PortabilityLayer::MemoryManager::GetInstance()->Release(m_tempBuffer); + } + + bool PixMapBlitEmitter::SpecifyFrame(const Rect &rect) + { + m_specFrame = rect; + return true; + } + + Rect PixMapBlitEmitter::ConstrainRegion(const Rect &rect) const + { + const Rect pixMapRect = m_pixMap->m_rect; + + const Rect2i rectInDrawSpace = Rect2i(rect) + m_drawOrigin; + + const Rect2i constrainedRectInDrawSpace = rectInDrawSpace.Intersect(Rect2i(pixMapRect)); + + // If this got completely culled away, return an empty rect, but avoid int truncation + if (!constrainedRectInDrawSpace.IsValid()) + return Rect::Create(rect.top, rect.left, rect.top, rect.left); + + // Otherwise, it should still be valid in the picture space + const Rect2i constrainedRectInPictSpace = constrainedRectInDrawSpace - m_drawOrigin; + return constrainedRectInPictSpace.ToShortRect(); + } + + void PixMapBlitEmitter::Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) + { + // FIXME: Detect different system palette (if we ever do that) + if (QDPictBlitSourceType_IsIndexed(sourceType)) + { + if (params.m_numColors == 256 && !memcmp(params.m_colors, StandardPalette::GetInstance()->GetColors(), sizeof(RGBAColor) * 256)) + m_sourceAndDestPalettesAreSame = true; + else + { + assert(false); + } + } + + m_blitType = sourceType; + m_params = params; + + m_constraintRegionWidth = params.m_constrainedRegionRight - params.m_constrainedRegionLeft; + m_constraintRegionStartIndex = params.m_constrainedRegionLeft - params.m_scanlineOriginX; + m_constraintRegionEndIndex = params.m_constrainedRegionRight - params.m_scanlineOriginX; + + const size_t firstCol = params.m_constrainedRegionLeft + m_drawOrigin.m_x - m_pixMap->m_rect.left; + const size_t firstRow = params.m_firstY + m_drawOrigin.m_y - m_pixMap->m_rect.top; + + m_outputIndexStart = firstRow * m_pixMap->GetPitch() + firstCol; + } + + void PixMapBlitEmitter::BlitScanlineAndAdvance(const void *data) + { + const int32_t crRight = m_params.m_constrainedRegionRight; + const int32_t crLeft = m_params.m_constrainedRegionLeft; + const size_t constraintRegionStartIndex = m_constraintRegionStartIndex; + const uint8_t *dataBytes = static_cast(data); + const size_t outputIndexStart = m_outputIndexStart; + const size_t planarSeparation = m_params.m_planarSeparation; + const size_t constraintRegionWidth = m_constraintRegionWidth; + + const uint8_t *paletteMapping = nullptr; + + const uint8_t staticMapping1Bit[] = { 0, 255 }; + + void *imageData = m_pixMap->GetPixelData(); + + if (m_pixMap->GetPixelFormat() == PixelFormat_8BitStandard) + { + switch (m_blitType) + { + case QDPictBlitSourceType_Indexed1Bit: + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 7 - (itemIndex & 7); + const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1; + static_cast(imageData)[i + outputIndexStart] = paletteMapping[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed2Bit: + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 6 - (2 * (itemIndex & 1)); + const int colorIndex = (dataBytes[itemIndex / 4] >> bitShift) & 0x3; + static_cast(imageData)[i + outputIndexStart] = paletteMapping[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed4Bit: + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 4 - (4 * (itemIndex & 1)); + const int colorIndex = (dataBytes[itemIndex / 2] >> bitShift) & 0xf; + static_cast(imageData)[i + outputIndexStart] = paletteMapping[colorIndex]; + } + break; + case QDPictBlitSourceType_Indexed8Bit: + if (m_sourceAndDestPalettesAreSame) + memcpy(static_cast(imageData) + outputIndexStart, dataBytes + constraintRegionStartIndex, m_constraintRegionWidth); + else + { + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + const uint8_t colorIndex = dataBytes[itemIndex]; + static_cast(imageData)[i + outputIndexStart] = paletteMapping[colorIndex]; + } + } + break; + case QDPictBlitSourceType_1Bit: + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const int bitShift = 7 - (itemIndex & 7); + const int colorIndex = (dataBytes[itemIndex / 8] >> bitShift) & 0x1; + static_cast(imageData)[i + outputIndexStart] = staticMapping1Bit[colorIndex]; + } + break; + case QDPictBlitSourceType_RGB15: + for (size_t i = 0; i < constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + const uint16_t item = *reinterpret_cast(dataBytes + itemIndex * 2); + uint8_t &outputItem = static_cast(imageData)[i + outputIndexStart]; + + outputItem = StandardPalette::GetInstance()->MapColorLUT((item >> 1) & 0xf, (item >> 6) & 0xf, (item >> 11) & 0x1f); + } + break; + case QDPictBlitSourceType_RGB24_Multiplane: + for (size_t i = 0; i < m_constraintRegionWidth; i++) + { + const size_t itemIndex = i + constraintRegionStartIndex; + + uint8_t &outputItem = static_cast(imageData)[i + outputIndexStart]; + + const uint8_t r = dataBytes[itemIndex]; + const uint8_t g = dataBytes[itemIndex + planarSeparation]; + const uint8_t b = dataBytes[itemIndex + planarSeparation * 2]; + + outputItem = StandardPalette::GetInstance()->MapColorLUT(r, g, b); + } + break; + default: + assert(false); + } + } + + m_outputIndexStart += m_pixMap->GetPitch(); + } + + bool PixMapBlitEmitter::AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) + { + m_tempBuffer = static_cast(PortabilityLayer::MemoryManager::GetInstance()->Alloc(buffer1Size + buffer2Size)); + if (!m_tempBuffer) + return false; + + buffer1 = m_tempBuffer; + buffer2 = m_tempBuffer + buffer1Size; + + return true; + } +} OSErr NewGWorld(GWorldPtr *gworld, int depth, Rect *bounds, CTabHandle colorTable, GDHandle device, int flags) { - PL_NotYetImplemented(); - return noErr; + return PortabilityLayer::QDManager::GetInstance()->NewGWorld(gworld, depth, *bounds, colorTable, device, flags); } void DisposeGWorld(GWorldPtr gworld) @@ -14,37 +244,90 @@ void DisposeGWorld(GWorldPtr gworld) PixMapHandle GetGWorldPixMap(GWorldPtr gworld) { - PL_NotYetImplemented(); - return nullptr; + if (!gworld) + return nullptr; + + return gworld->m_port.GetPixMap(); } void LockPixels(PixMapHandle pixmap) { - PL_NotYetImplemented(); + (void)pixmap; } PicHandle GetPicture(short resID) { - PL_NotYetImplemented(); - return nullptr; + return reinterpret_cast(PortabilityLayer::ResourceManager::GetInstance()->GetResource('PICT', resID)); } void OffsetRect(Rect *rect, int right, int down) { - PL_NotYetImplemented(); + rect->left += right; + rect->right += right; + rect->top += down; + rect->bottom += down; } void DrawPicture(PicHandle pict, Rect *bounds) { - PL_NotYetImplemented(); + if (!pict) + return; + + PicPtr picPtr = *pict; + + if (!picPtr) + return; + + const Rect picRect = picPtr->picFrame.ToRect(); + + if (bounds->right - bounds->left != picRect.right - picRect.left || bounds->bottom - bounds->top != picRect.bottom - picRect.top) + { + // Scaled pict draw (not supported) + assert(false); + return; + } + + PortabilityLayer::QDManager *qdManager = PortabilityLayer::QDManager::GetInstance(); + PortabilityLayer::QDPort *port; + qdManager->GetPort(&port, nullptr); + + if (!port) + return; + + PortabilityLayer::PixMapImpl *pixMap = static_cast(*port->GetPixMap()); + + long handleSize = GetHandleSize(reinterpret_cast(pict)); + PortabilityLayer::MemReaderStream stream(picPtr, handleSize); + + switch (pixMap->GetPixelFormat()) + { + case PortabilityLayer::PixelFormat_8BitStandard: + { + PortabilityLayer::PixMapBlitEmitter blitEmitter(PortabilityLayer::Vec2i(bounds->left, bounds->top), pixMap); + PortabilityLayer::QDPictDecoder decoder; + + decoder.DecodePict(&stream, &blitEmitter); + } + break; + default: + // TODO: Implement higher-resolution pixel blitters + assert(false); + return; + }; } void GetGWorld(CGrafPtr *gw, GDHandle *gdHandle) { - PL_NotYetImplemented(); + PortabilityLayer::QDPort *port; + PortabilityLayer::QDManager::GetInstance()->GetPort(&port, gdHandle); + + CGrafPtr grafPtr = reinterpret_cast(port); + assert(&grafPtr->m_port == port); + + *gw = grafPtr; } void SetGWorld(CGrafPtr gw, GDHandle gdHandle) { - PL_NotYetImplemented(); + PortabilityLayer::QDManager::GetInstance()->SetPort(&gw->m_port, gdHandle); } diff --git a/PortabilityLayer/PLQDOffscreen.h b/PortabilityLayer/PLQDOffscreen.h index 55c889d..106b049 100644 --- a/PortabilityLayer/PLQDOffscreen.h +++ b/PortabilityLayer/PLQDOffscreen.h @@ -9,14 +9,7 @@ struct ColorTable { }; -struct PixMap -{ -}; - -struct Picture -{ - Rect picFrame; -}; +struct PixMap; typedef ColorTable *CTabPtr; typedef CTabPtr *CTabHandle; diff --git a/PortabilityLayer/PLQuickdraw.cpp b/PortabilityLayer/PLQuickdraw.cpp index 49ba898..308857a 100644 --- a/PortabilityLayer/PLQuickdraw.cpp +++ b/PortabilityLayer/PLQuickdraw.cpp @@ -1,5 +1,15 @@ #include "PLQuickdraw.h" +#include "QDManager.h" +#include "QDState.h" #include "DisplayDeviceManager.h" +#include "MMHandleBlock.h" +#include "ResourceManager.h" +#include "ResTypeID.h" +#include "RGBAColor.h" +#include "WindowManager.h" +#include "QDGraf.h" +#include "QDPixMap.h" +#include "QDUtils.h" void GetPort(GrafPtr *graf) { @@ -8,15 +18,19 @@ void GetPort(GrafPtr *graf) void SetPort(GrafPtr graf) { - PL_NotYetImplemented(); + PortabilityLayer::QDManager *qd = PortabilityLayer::QDManager::GetInstance(); + + GDHandle device; + qd->GetPort(nullptr, &device); + qd->SetPort(graf, device); } -void BeginUpdate(GrafPtr graf) +void BeginUpdate(WindowPtr graf) { PL_NotYetImplemented(); } -void EndUpdate(GrafPtr graf) +void EndUpdate(WindowPtr graf) { PL_NotYetImplemented(); } @@ -65,7 +79,11 @@ GDHandle GetMainDevice() void SetPortWindowPort(WindowPtr window) { - PL_NotYetImplemented(); + PortabilityLayer::WindowManager *wm = PortabilityLayer::WindowManager::GetInstance(); + + GDevice **device = wm->GetWindowDevice(window); + + PortabilityLayer::QDManager::GetInstance()->SetPort(&window->m_port, device); } void SetPortDialogPort(Dialog *dialog) @@ -73,20 +91,19 @@ void SetPortDialogPort(Dialog *dialog) PL_NotYetImplemented(); } - void TextSize(int sz) { - PL_NotYetImplemented(); + PortabilityLayer::QDManager::GetInstance()->GetState()->m_textSize = sz; } void TextFace(int face) { - PL_NotYetImplemented(); + PortabilityLayer::QDManager::GetInstance()->GetState()->m_textFace = face; } void TextFont(int fontID) { - PL_NotYetImplemented(); + PortabilityLayer::QDManager::GetInstance()->GetState()->m_fontID = fontID; } int TextWidth(const PLPasStr &str, int firstChar1Based, int length) @@ -110,14 +127,69 @@ void SetOrigin(int x, int y) PL_NotYetImplemented(); } +namespace +{ + static bool SystemColorToRGBAColor(SystemColorID color, PortabilityLayer::RGBAColor &rgbaColor) + { + switch (color) + { + default: + return false; + case whiteColor: + rgbaColor.r = rgbaColor.g = rgbaColor.b = 255; + break; + case blackColor: + rgbaColor.r = rgbaColor.g = rgbaColor.b = 0; + break; + case yellowColor: + rgbaColor.r = rgbaColor.g = 255; + rgbaColor.b = 0; + break; + case magentaColor: + rgbaColor.r = rgbaColor.b = 255; + rgbaColor.g = 0; + break; + case redColor: + rgbaColor.r = 255; + rgbaColor.g = rgbaColor.b = 0; + break; + case cyanColor: + rgbaColor.g = rgbaColor.b = 255; + rgbaColor.r = 0; + break; + case greenColor: + rgbaColor.g = 255; + rgbaColor.r = rgbaColor.b = 0; + break; + case blueColor: + rgbaColor.b = 255; + rgbaColor.r = rgbaColor.g = 0; + break; + } + rgbaColor.a = 255; + + return true; + } +} + void ForeColor(SystemColorID color) { - PL_NotYetImplemented(); + PortabilityLayer::RGBAColor rgbaColor; + if (SystemColorToRGBAColor(color, rgbaColor)) + { + PortabilityLayer::QDState *qdState = PortabilityLayer::QDManager::GetInstance()->GetState(); + qdState->SetForeColor(rgbaColor); + } } void BackColor(SystemColorID color) { - PL_NotYetImplemented(); + PortabilityLayer::RGBAColor rgbaColor; + if (SystemColorToRGBAColor(color, rgbaColor)) + { + PortabilityLayer::QDState *qdState = PortabilityLayer::QDManager::GetInstance()->GetState(); + qdState->SetBackColor(rgbaColor); + } } void GetForeColor(RGBColor *color) @@ -142,7 +214,59 @@ void DrawString(const PLPasStr &str) void PaintRect(const Rect *rect) { - PL_NotYetImplemented(); + if (!rect->IsValid()) + return; + + PortabilityLayer::QDPort *qdPort; + PortabilityLayer::QDManager::GetInstance()->GetPort(&qdPort, nullptr); + + PortabilityLayer::PixelFormat pixelFormat = qdPort->GetPixelFormat(); + + Rect constrainedRect = *rect; + + PortabilityLayer::QDState *qdState = qdPort->GetState(); + + if (qdState->m_clipRegion) + { + const Region ®ion = **qdState->m_clipRegion; + + if (region.size > sizeof(Region)) + PL_NotYetImplemented(); + + constrainedRect = constrainedRect.Intersect(region.rect); + } + + constrainedRect = constrainedRect.Intersect(qdPort->GetRect()); + + if (!constrainedRect.IsValid()) + return; + + PortabilityLayer::PixMapImpl *pixMap = static_cast(*qdPort->GetPixMap()); + const size_t pitch = pixMap->GetPitch(); + const size_t firstIndex = static_cast(constrainedRect.top) * pitch + static_cast(constrainedRect.left); + const size_t numLines = static_cast(constrainedRect.bottom - constrainedRect.top); + const size_t numCols = static_cast(constrainedRect.right - constrainedRect.left); + uint8_t *pixData = static_cast(pixMap->GetPixelData()); + + switch (pixelFormat) + { + case PortabilityLayer::PixelFormat_8BitStandard: + { + const uint8_t color = qdState->ResolveForeColor8(nullptr, 0); + + size_t scanlineIndex = 0; + for (size_t ln = 0; ln < numLines; ln++) + { + const size_t firstLineIndex = firstIndex + ln * pitch; + for (size_t col = 0; col < numCols; col++) + pixData[firstLineIndex + col] = color; + } + } + break; + default: + PL_NotYetImplemented(); + return; + } } void PaintOval(const Rect *rect) @@ -157,7 +281,14 @@ void PaintRgn(RgnHandle region) void ClipRect(const Rect *rect) { - PL_NotYetImplemented(); + if (!rect->IsValid()) + return; + + PortabilityLayer::QDState *qdState = PortabilityLayer::QDManager::GetInstance()->GetState(); + if (!qdState->m_clipRegion) + qdState->m_clipRegion = PortabilityLayer::QDUtils::CreateRegion(*rect); + else + PortabilityLayer::QDUtils::ResetRegionToRect(qdState->m_clipRegion, *rect); } void FrameRect(const Rect *rect) @@ -239,7 +370,17 @@ Pattern *GetQDGlobalsBlack(Pattern *pattern) void GetIndPattern(Pattern *pattern, int patListID, int index) { - PL_NotYetImplemented(); + if (index < 1) + return; + + PortabilityLayer::MMHandleBlock *patternList = PortabilityLayer::ResourceManager::GetInstance()->GetResource('PAT#', patListID); + const uint8_t *patternRes = static_cast(patternList->m_contents); + + int numPatterns = (patternRes[0] << 8) | patternRes[1]; + if (index > numPatterns) + return; + + memcpy(pattern, patternRes + 2 + (index - 1) * 8, 8); } diff --git a/PortabilityLayer/PLQuickdraw.h b/PortabilityLayer/PLQuickdraw.h index bf26b47..98ef837 100644 --- a/PortabilityLayer/PLQuickdraw.h +++ b/PortabilityLayer/PLQuickdraw.h @@ -3,6 +3,8 @@ #define __PL_QUICKDRAW_H__ #include "PLCore.h" +#include "QDGraf.h" +#include "SharedTypes.h" struct Dialog; @@ -100,12 +102,9 @@ struct CIcon { }; -struct GDevice -{ -}; - struct BitMap { + Rect m_rect; }; struct RGBColor @@ -121,7 +120,8 @@ typedef GDPtr *GDHandle; typedef CIcon *CIconPtr; typedef CIconPtr *CIconHandle; -typedef WindowPtr GrafPtr; +typedef PortabilityLayer::QDPort GrafPort; +typedef GrafPort *GrafPtr; typedef Byte Pattern[8]; @@ -130,8 +130,8 @@ void SetPort(GrafPtr graf); void SetPortWindowPort(WindowPtr window); void SetPortDialogPort(Dialog *dialog); -void BeginUpdate(GrafPtr graf); -void EndUpdate(GrafPtr graf); +void BeginUpdate(WindowPtr graf); +void EndUpdate(WindowPtr graf); OSErr GetIconSuite(Handle *suite, short resID, IconSuiteFlags flags); OSErr PlotIconSuite(Rect *rect, IconAlignmentType alignType, IconTransformType transformType, Handle iconSuite); diff --git a/PortabilityLayer/PLResources.cpp b/PortabilityLayer/PLResources.cpp index de4a10a..199bc47 100644 --- a/PortabilityLayer/PLResources.cpp +++ b/PortabilityLayer/PLResources.cpp @@ -1,17 +1,28 @@ #include "PLResources.h" +#include "MemoryManager.h" #include "MMHandleBlock.h" #include "ResourceManager.h" #include "ResourceCompiledRef.h" +#include + void DetachResource(Handle hdl) { - PL_NotYetImplemented(); + if (!hdl) + return; + + PortabilityLayer::MMHandleBlock *block = reinterpret_cast(hdl); + assert(block->m_rmSelfRef); + assert(block->m_rmSelfRef->m_handle == block); + block->m_rmSelfRef->m_handle = nullptr; + block->m_rmSelfRef = nullptr; } void ReleaseResource(Handle hdl) { - PL_NotYetImplemented(); + DetachResource(hdl); + DisposeHandle(hdl); } short CurResFile() diff --git a/PortabilityLayer/PLSound.cpp b/PortabilityLayer/PLSound.cpp index 49cb59d..037c02d 100644 --- a/PortabilityLayer/PLSound.cpp +++ b/PortabilityLayer/PLSound.cpp @@ -1,5 +1,203 @@ #include "PLSound.h" +#include "ClientAudioChannelContext.h" +#include "HostAudioChannel.h" +#include "HostAudioDriver.h" +#include "MemoryManager.h" +#include "HostMutex.h" +#include "HostSystemServices.h" +#include "HostThreadEvent.h" + +#include + +namespace PortabilityLayer +{ + class AudioChannelImpl : public SndChannel, public ClientAudioChannelContext + { + public: + explicit AudioChannelImpl(PortabilityLayer::HostAudioChannel *channel, SndCallBackUPP callback, HostThreadEvent *threadEvent, HostMutex *mutex); + ~AudioChannelImpl(); + + bool PushCommand(const SndCommand &command, bool blocking); + + void NotifyBufferFinished() override; + + private: + enum WorkingState + { + State_Idle, // No thread is playing sound, the sound thread is out of work + State_PlayingAsync, // Sound thread is playing sound. When it finishes, it will digest queue events under a lock. + State_PlayingBlocked, // Sound thread is playing sound. When it finishes, it will transition to Idle and fire the thread event. + State_FlushStarting, // Sound thread is aborting. When it aborts, it will transition to Idle and fire the thread event. + }; + + static const unsigned int kMaxQueuedCommands = 64; + + void DigestQueueItems(); + void DigestBufferCommand(const void *dataPointer); + + PortabilityLayer::HostAudioChannel *m_audioChannel; + SndCallBackUPP m_callback; + + HostMutex *m_mutex; + HostThreadEvent *m_threadEvent; + + SndCommand m_commandQueue[kMaxQueuedCommands]; + size_t m_numQueuedCommands; + size_t m_nextInsertCommandPos; + size_t m_nextDequeueCommandPos; + WorkingState m_state; + bool m_isIdle; + }; + + AudioChannelImpl::AudioChannelImpl(PortabilityLayer::HostAudioChannel *channel, SndCallBackUPP callback, HostThreadEvent *threadEvent, HostMutex *mutex) + : m_audioChannel(channel) + , m_callback(callback) + , m_threadEvent(threadEvent) + , m_mutex(mutex) + , m_nextInsertCommandPos(0) + , m_nextDequeueCommandPos(0) + , m_numQueuedCommands(0) + , m_state(State_Idle) + , m_isIdle(false) + { + m_audioChannel->SetClientAudioChannelContext(this); + } + + AudioChannelImpl::~AudioChannelImpl() + { + m_mutex->Destroy(); + m_threadEvent->Destroy(); + } + + void AudioChannelImpl::NotifyBufferFinished() + { + m_mutex->Lock(); + + if (m_state == State_PlayingAsync) + { + m_state = State_Idle; + DigestQueueItems(); + } + else if (m_state == State_PlayingBlocked || m_state == State_FlushStarting) + { + m_state = State_Idle; + m_threadEvent->Signal(); + } + + m_mutex->Unlock(); + } + + void AudioChannelImpl::DigestQueueItems() + { + m_mutex->Lock(); + + assert(m_state == State_Idle); + + while (m_numQueuedCommands > 0) + { + const SndCommand &command = m_commandQueue[m_nextDequeueCommandPos]; + m_numQueuedCommands--; + m_nextDequeueCommandPos = (m_nextDequeueCommandPos + 1) % static_cast(kMaxQueuedCommands); + + switch (command.cmd) + { + case nullCmd: + break; + case bufferCmd: + DigestBufferCommand(reinterpret_cast(command.param2)); + assert(m_state == State_PlayingAsync); + m_mutex->Unlock(); + return; + case callBackCmd: + { + SndCommand commandCopy = command; + m_callback(this, &commandCopy); + } + break; + default: + case flushCmd: + case quietCmd: + assert(false); // These shouldn't be in the queue + break; + } + } + m_mutex->Unlock(); + } + + void AudioChannelImpl::DigestBufferCommand(const void *dataPointer) + { + struct BufferHeader + { + BEUInt32_t m_samplePtr; + BEUInt32_t m_length; + BEFixed32_t m_sampleRate; + BEUInt32_t m_loopStart; + BEUInt32_t m_loopEnd; + uint8_t m_encoding; + uint8_t m_baseFrequency; + }; + + BufferHeader bufferHeader; + + PL_STATIC_ASSERT(sizeof(BufferHeader) >= 22); + + memcpy(&bufferHeader, dataPointer, 22); + + const uint32_t length = bufferHeader.m_length; + + m_audioChannel->PostBuffer(static_cast(dataPointer) + 22, length); + m_state = State_PlayingAsync; + } + + bool AudioChannelImpl::PushCommand(const SndCommand &command, bool failIfFull) + { + bool digestOnThisThread = false; + + m_mutex->Lock(); + if (m_numQueuedCommands == kMaxQueuedCommands) + { + if (failIfFull) + { + m_mutex->Unlock(); + return false; + } + else + { + assert(m_state == State_PlayingAsync); + + if (m_numQueuedCommands == kMaxQueuedCommands) + { + m_state = State_PlayingBlocked; + m_mutex->Unlock(); + + m_threadEvent->Wait(); + + m_mutex->Lock(); + + assert(m_state == State_Idle); + digestOnThisThread = true; + } + } + } + else + { + if (m_state == State_Idle) + digestOnThisThread = true; + } + + m_commandQueue[m_nextInsertCommandPos] = command; + m_nextInsertCommandPos = (m_nextInsertCommandPos + 1) % static_cast(kMaxQueuedCommands); + m_numQueuedCommands++; + m_mutex->Unlock(); + + if (digestOnThisThread) + DigestQueueItems(); + + return true; + } +} + OSErr GetDefaultOutputVolume(long *vol) { short leftVol = 0x100; @@ -29,19 +227,71 @@ void DisposeSndCallBackUPP(SndCallBackUPP upp) OSErr SndNewChannel(SndChannelPtr *outChannel, SndSynthType synthType, int initFlags, SndCallBackUPP callback) { - PL_NotYetImplemented(); + PortabilityLayer::MemoryManager *mm = PortabilityLayer::MemoryManager::GetInstance(); + void *storage = mm->Alloc(sizeof(PortabilityLayer::AudioChannelImpl)); + if (!storage) + return mFulErr; + + PortabilityLayer::HostAudioDriver *audioDriver = PortabilityLayer::HostAudioDriver::GetInstance(); + PortabilityLayer::HostAudioChannel *audioChannel = audioDriver->CreateChannel(); + if (!audioChannel) + { + mm->Release(storage); + return genericErr; + } + + PortabilityLayer::HostMutex *mutex = PortabilityLayer::HostSystemServices::GetInstance()->CreateMutex(); + if (!mutex) + { + audioChannel->Destroy(); + mm->Release(storage); + return genericErr; + } + + PortabilityLayer::HostThreadEvent *threadEvent = PortabilityLayer::HostSystemServices::GetInstance()->CreateThreadEvent(true, false); + if (!threadEvent) + { + mutex->Destroy(); + audioChannel->Destroy(); + mm->Release(storage); + return genericErr; + } + + *outChannel = new (storage) PortabilityLayer::AudioChannelImpl(audioChannel, callback, threadEvent, mutex); + return noErr; } OSErr SndDisposeChannel(SndChannelPtr channel, Boolean flush) { - PL_NotYetImplemented(); + if (flush) + { + SndCommand cmd; + cmd.cmd = flushCmd; + cmd.param1 = cmd.param2 = 0; + + SndDoImmediate(channel, &cmd); + + cmd.cmd = quietCmd; + cmd.param1 = cmd.param2 = 0; + SndDoImmediate(channel, &cmd); + } + + PortabilityLayer::AudioChannelImpl *audioChannelImpl = static_cast(channel); + audioChannelImpl->~AudioChannelImpl(); + + PortabilityLayer::MemoryManager::GetInstance()->Release(audioChannelImpl); + return noErr; } OSErr SndDoCommand(SndChannelPtr channel, const SndCommand *command, Boolean failIfFull) { - PL_NotYetImplemented(); + PortabilityLayer::AudioChannelImpl *audioChannelImpl = static_cast(channel); + + if (!audioChannelImpl->PushCommand(*command, failIfFull == 0)) + return queueFull; + return noErr; } diff --git a/PortabilityLayer/PixelFormat.h b/PortabilityLayer/PixelFormat.h new file mode 100644 index 0000000..1869ebb --- /dev/null +++ b/PortabilityLayer/PixelFormat.h @@ -0,0 +1,15 @@ +#pragma once + +namespace PortabilityLayer +{ + enum PixelFormat + { + PixelFormat_Invalid, + + PixelFormat_8BitStandard, + PixelFormat_8BitCustom, + PixelFormat_RGB555, + PixelFormat_RGB24, + PixelFormat_RGB32, + }; +} diff --git a/PortabilityLayer/PortabilityLayer.vcxproj b/PortabilityLayer/PortabilityLayer.vcxproj index e4789d0..83b1e4e 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj +++ b/PortabilityLayer/PortabilityLayer.vcxproj @@ -127,16 +127,21 @@ + + + + + @@ -150,6 +155,7 @@ + @@ -164,8 +170,24 @@ + + + + + + + + + + + + + + + + @@ -184,6 +206,9 @@ + + + @@ -191,6 +216,8 @@ + + @@ -200,6 +227,7 @@ + @@ -227,9 +255,21 @@ + + + + + + + + + + + + diff --git a/PortabilityLayer/PortabilityLayer.vcxproj.filters b/PortabilityLayer/PortabilityLayer.vcxproj.filters index d23ec8a..1117a0a 100644 --- a/PortabilityLayer/PortabilityLayer.vcxproj.filters +++ b/PortabilityLayer/PortabilityLayer.vcxproj.filters @@ -234,6 +234,87 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + @@ -347,5 +428,44 @@ Source Files + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file diff --git a/PortabilityLayer/QDGraf.cpp b/PortabilityLayer/QDGraf.cpp new file mode 100644 index 0000000..e69de29 diff --git a/PortabilityLayer/QDGraf.h b/PortabilityLayer/QDGraf.h new file mode 100644 index 0000000..8f651ae --- /dev/null +++ b/PortabilityLayer/QDGraf.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include "PixelFormat.h" +#include "QDState.h" +#include "QDPort.h" + +struct PixMap; +struct Rect; + +struct CGraf +{ + CGraf() + : m_port(PortabilityLayer::QDPortType_CGraf) + { + } + + int Init(const Rect &rect, PortabilityLayer::PixelFormat pixelFormat) + { + if (int errorCode = m_port.Init(rect, pixelFormat)) + return errorCode; + + return 0; + } + + // Must be the first item + PortabilityLayer::QDPort m_port; +}; + +namespace PortabilityLayer +{ + class CGrafImpl final : public CGraf + { + }; +} diff --git a/PortabilityLayer/QDManager.cpp b/PortabilityLayer/QDManager.cpp new file mode 100644 index 0000000..766d386 --- /dev/null +++ b/PortabilityLayer/QDManager.cpp @@ -0,0 +1,112 @@ +#include "QDManager.h" + +#include "MemoryManager.h" +#include "PLCore.h" +#include "PLQDOffscreen.h" +#include "QDGraf.h" +#include "QDState.h" + +namespace PortabilityLayer +{ + class QDManagerImpl final : public QDManager + { + public: + QDManagerImpl(); + + void Init() override; + void GetPort(QDPort **port, GDevice ***gdHandle) override; + void SetPort(QDPort *gw, GDevice **gdHandle) override; + int NewGWorld(CGraf **gw, int depth, const Rect &bounds, ColorTable **colorTable, GDevice **device, int flags) override; + QDState *GetState() override; + + static QDManagerImpl *GetInstance(); + + private: + QDPort *m_port; + GDHandle m_gdHandle; + + static QDManagerImpl ms_instance; + }; + + QDManagerImpl::QDManagerImpl() + : m_port(nullptr) + , m_gdHandle(nullptr) + { + } + + void QDManagerImpl::Init() + { + } + + void QDManagerImpl::GetPort(QDPort **port, GDevice ***gdHandle) + { + if (port) + *port = m_port; + if (gdHandle) + *gdHandle = m_gdHandle; + } + + void QDManagerImpl::SetPort(QDPort *gw, GDevice **gdHandle) + { + m_port = gw; + m_gdHandle = gdHandle; + } + + int QDManagerImpl::NewGWorld(CGraf **gw, int depth, const Rect &bounds, ColorTable **colorTable, GDevice **device, int flags) + { + PixelFormat pixelFormat; + + switch (depth) + { + case 8: + pixelFormat = (colorTable == nullptr) ? PixelFormat_8BitStandard : PixelFormat_8BitCustom; + break; + case 16: + pixelFormat = PixelFormat_RGB555; + break; + case 32: + pixelFormat = PixelFormat_RGB32; + break; + default: + return genericErr; + } + + if (depth != 8) + return genericErr; + + void *grafStorage = MemoryManager::GetInstance()->Alloc(sizeof(CGrafImpl)); + if (!grafStorage) + return mFulErr; + + if (!bounds.IsValid()) + return genericErr; + + CGrafImpl *graf = new (grafStorage) CGrafImpl(); + int initError = graf->Init(bounds, pixelFormat); + if (initError) + { + DisposeGWorld(graf); + return initError; + } + + *gw = graf; + return noErr; + } + + QDState *QDManagerImpl::GetState() + { + return m_port->GetState(); + } + + QDManagerImpl *QDManagerImpl::GetInstance() + { + return &ms_instance; + } + + QDManagerImpl QDManagerImpl::ms_instance; + + QDManager *QDManager::GetInstance() + { + return QDManagerImpl::GetInstance(); + } +} diff --git a/PortabilityLayer/QDManager.h b/PortabilityLayer/QDManager.h new file mode 100644 index 0000000..cd691a0 --- /dev/null +++ b/PortabilityLayer/QDManager.h @@ -0,0 +1,25 @@ +#pragma once + +struct ColorTable; +struct CGraf; +struct GDevice; +struct Rect; + +namespace PortabilityLayer +{ + class QDPort; + struct QDState; + + class QDManager + { + public: + virtual void Init() = 0; + virtual void GetPort(QDPort **gw, GDevice ***gdHandle) = 0; + virtual void SetPort(QDPort *gw, GDevice **gdHandle) = 0; + virtual int NewGWorld(CGraf **gw, int depth, const Rect &bounds, ColorTable **colorTable, GDevice **device, int flags) = 0; + + virtual QDState *GetState() = 0; + + static QDManager *GetInstance(); + }; +} diff --git a/PortabilityLayer/QDPictDecoder.cpp b/PortabilityLayer/QDPictDecoder.cpp new file mode 100644 index 0000000..421e367 --- /dev/null +++ b/PortabilityLayer/QDPictDecoder.cpp @@ -0,0 +1,734 @@ +#include "CoreDefs.h" +#include "QDPictDecoder.h" +#include "QDPictEmitContext.h" +#include "QDPictHeader.h" +#include "QDPictOpcodes.h" +#include "QDPictEmitScanlineParameters.h" +#include "IOStream.h" +#include "RGBAColor.h" +#include "Vec2i.h" + +#include +#include + +namespace +{ + static void DecodeClutItemChannel(uint8_t &outChannel, const uint8_t *color16) + { + const int colorHigh = color16[0]; + const int colorLow = color16[1]; + + const int lowDelta = colorLow - colorHigh; + if (lowDelta < -128) + outChannel = static_cast(colorHigh - 1); + else if (lowDelta > 128) + outChannel = static_cast(colorHigh + 1); + outChannel = static_cast(colorHigh); + } + + static void DecodeClutItem(PortabilityLayer::RGBAColor &decoded, const BEColorTableItem &clutItem) + { + DecodeClutItemChannel(decoded.r, clutItem.m_red); + DecodeClutItemChannel(decoded.g, clutItem.m_green); + DecodeClutItemChannel(decoded.b, clutItem.m_blue); + decoded.a = 255; + } +} + +namespace PortabilityLayer +{ + QDPictDecoder::QDPictDecoder() + : m_stream(nullptr) + { + } + + bool QDPictDecoder::DecodePict(IOStream *stream, QDPictEmitContext *emitContext) + { + QDPictHeader header; + + if (!header.Load(stream)) + return false; + + emitContext->SpecifyFrame(header.GetFrame()); + + const Rect constrainedFrame = emitContext->ConstrainRegion(header.GetFrame()); + Rect activeFrame = constrainedFrame; + + BERect scratchBERect; + Rect scratchRect; + BEUInt16_t scratchUInt16; + BEUInt32_t scratchUInt32; + + const int pictVersion = header.GetVersion(); + if (pictVersion != 1 && pictVersion != 2) + return false; + + const size_t opcodeSize = (pictVersion == 1) ? 1 : 2; + + uint8_t scratchBytes[64]; + + for (;;) + { + if (stream->Read(scratchBytes, opcodeSize) != opcodeSize) + return false; + + bool finished = false; + + uint16_t opcode = scratchBytes[0]; + if (header.GetVersion() == 2) + opcode = (opcode << 8) | scratchBytes[1]; + + int rasterOpErrorCode = 0; + + switch (opcode) + { + case QDOpcodes::kNoop: + break; + case QDOpcodes::kClipRegion: + if (stream->Read(scratchBytes, 10) != 10 || scratchBytes[0] != 0 || scratchBytes[1] != 10) + return false; // Unknown format region + + PL_STATIC_ASSERT(sizeof(scratchBERect) == 8); + + memcpy(&scratchBERect, scratchBytes + 2, 8); + scratchRect = scratchBERect.ToRect(); + + if (!scratchRect.IsValid()) + return false; + + break; + case QDOpcodes::kShortComment: + if (!stream->SeekCurrent(2)) + return false; + break; + case QDOpcodes::kLongComment: + { + if (stream->Read(scratchBytes, 4) != 4) + return false; + + const uint16_t commentKind = (scratchBytes[0] << 8) | scratchBytes[1]; + const uint16_t commentSize = (scratchBytes[2] << 8) | scratchBytes[3]; + + if (!stream->SeekCurrent(commentSize)) + return false; + } + break; + case QDOpcodes::kBitsRect: + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), false, false, false, activeFrame, emitContext); + if (rasterOpErrorCode) + return false; + break; + case QDOpcodes::kPackBitsRect: + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, false, activeFrame, emitContext); + if (rasterOpErrorCode) + return false; + break; + case QDOpcodes::kPackBitsRgn: + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, false, activeFrame, emitContext); + if (rasterOpErrorCode) + return false; + break; + case QDOpcodes::kDirectBitsRect: + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, false, true, activeFrame, emitContext); + if (rasterOpErrorCode) + return false; + break; + case QDOpcodes::kDirectBitsRgn: + rasterOpErrorCode = ProcessRasterOp(stream, header.GetVersion(), true, true, true, activeFrame, emitContext); + if (rasterOpErrorCode) + return false; + break; + case QDOpcodes::kDefaultHilite: + break; + case QDOpcodes::kOpColor: + if (!stream->SeekCurrent(6)) + return false; + break; + case QDOpcodes::kEndOfPicture: + finished = true; + break; + default: + // Unknown opcode + return false; + } + + if (finished) + return true; + } + } + + int QDPictDecoder::ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &constraintRect, QDPictEmitContext *context) + { + uint16_t rowSizeBytes = 0; + + bool isPixMap = false; + if (isDirect) + { + isPixMap = true; + // Skip base address + if (!stream->SeekCurrent(4)) + return 1; + + if (stream->Read(&rowSizeBytes, sizeof(uint16_t)) != sizeof(uint16_t)) + return 2; + + ByteSwap::BigUInt16(rowSizeBytes); + + rowSizeBytes &= 0x7fff; + } + else + { + if (stream->Read(&rowSizeBytes, sizeof(uint16_t)) != sizeof(uint16_t)) + return 3; + + ByteSwap::BigUInt16(rowSizeBytes); + + if (pictVersion == 2) + { + if ((rowSizeBytes & 0x8000) != 0) + { + isPixMap = true; + rowSizeBytes &= 0x7fff; + } + // ... else this is a bitmap + } + } + + Rect srcRect; + Rect destRect; + uint16_t transferMode; + + BEColorTableHeader clutHeader; + BEColorTableItem clutItems[256]; + + BEPixMap pixMapBE; + + int packType = 0; + + RGBAColor colors[256]; + size_t numColors = 0; + + if (!isPixMap) + { + BEBitMap bitMap; + if (stream->Read(&bitMap, sizeof(BEBitMap)) != sizeof(BEBitMap)) + return 4; + + packType = (isPackedFlag && rowSizeBytes >= 8) ? 0 : 1; + + pixMapBE.m_bounds = bitMap.m_bounds; + pixMapBE.m_version = 2; + pixMapBE.m_packType = packType; + pixMapBE.m_hRes = 72 << 16; + pixMapBE.m_vRes = 72 << 16; + pixMapBE.m_vRes = 72 << 16; + pixMapBE.m_pixelType = 16; // FIXME: Use direct instead + pixMapBE.m_pixelSize = 1; + pixMapBE.m_componentCount = 1; + pixMapBE.m_componentSize = 1; + pixMapBE.m_planeSizeBytes = 0; + pixMapBE.m_clutHandle = 0; + pixMapBE.m_unused = 0; + + BERect srcRectBE; + if (stream->Read(&srcRectBE, sizeof(BERect)) != sizeof(BERect)) + return 5; + + BERect destRectBE; + if (stream->Read(&destRectBE, sizeof(BERect)) != sizeof(BERect)) + return 6; + + srcRect = srcRectBE.ToRect(); + destRect = destRectBE.ToRect(); + + if (!srcRect.IsValid()) + return 7; + + if (!destRect.IsValid()) + return 8; + + if (stream->Read(&transferMode, 2) != 2) + return 9; + + ByteSwap::BigUInt16(transferMode); + + if (hasRegion) + { + BEUInt16_t regionSize; + if (stream->Read(®ionSize, 2) != 2) + return 10; + + if (regionSize < 2) + return 11; + + if (!stream->SeekCurrent(regionSize - 2)) + return 12; + } + + colors[0].r = colors[0].g = colors[0].b = 255; + colors[0].a = 255; + + colors[1].r = colors[1].g = colors[1].b = 0; + colors[1].a = 255; + + numColors = 0; + } + else + { + // If rowBytes < 8 or pack type == 1, data is unpacked + // If pack type == 2, pad byte of 32-bit data is dropped (direct pixels only, 32-bit images) + // If pack type == 3, 16-bit RLE + // If pack type == 4, 8-bit planar component RLE + + PL_STATIC_ASSERT(sizeof(BEPixMap) == 44); + + if (stream->Read(&pixMapBE, sizeof(BEPixMap)) != sizeof(BEPixMap)) + return 13; + + if (isDirect) + { + } + else + { + if (stream->Read(&clutHeader, sizeof(BEColorTableHeader)) != sizeof(BEColorTableHeader)) + return 14; + + const uint16_t numItemsMinusOne = clutHeader.m_numItemsMinusOne; + if (numItemsMinusOne > 255) + return 15; + + numColors = clutHeader.m_numItemsMinusOne + 1; + + if (stream->Read(clutItems, sizeof(BEColorTableItem) * numColors) != sizeof(BEColorTableItem) * numColors) + return 16; + + for (size_t i = 0; i < numColors; i++) + DecodeClutItem(colors[i], clutItems[i]); + } + + BERect srcRectBE; + if (stream->Read(&srcRectBE, sizeof(BERect)) != sizeof(BERect)) + return 17; + + BERect destRectBE; + if (stream->Read(&destRectBE, sizeof(BERect)) != sizeof(BERect)) + return 18; + + srcRect = srcRectBE.ToRect(); + destRect = destRectBE.ToRect(); + + if (!srcRect.IsValid() || !destRect.IsValid()) + return 19; + + if (stream->Read(&transferMode, 2) != 2) + return 20; + + ByteSwap::BigUInt16(transferMode); + + if (!isPackedFlag && rowSizeBytes >= 8 && pixMapBE.m_packType != 1) + return 21; + + if (hasRegion) + { + uint16_t regionSize; + if (stream->Read(®ionSize, 2) != 2) + return 22; + + ByteSwap::BigUInt16(regionSize); + + if (regionSize < 2) + return 23; + + if (!stream->SeekCurrent(regionSize - 2)) + return 24; + } + + const unsigned int pixelSize = pixMapBE.m_pixelSize; + const unsigned int componentCount = pixMapBE.m_componentCount; + const unsigned int componentSize = pixMapBE.m_componentSize; + + packType = pixMapBE.m_packType; + if (isDirect) + { + if (packType == 0) + { + switch (pixelSize) + { + case 16: + packType = 3; + break; + case 32: + packType = 4; + break; + default: + break; + } + } + + if (packType == 4) + { + if (pixMapBE.m_componentCount != 3) + return 25; + + if (pixMapBE.m_componentSize != 8) + return 26; + + if (pixelSize != 32) + return 27; + } + else if (packType == 3) + { + if (componentCount != 3) + return 28; + if (componentSize != 5) + return 29; + + if (pixelSize != 16) + return 30; + } + else + { + switch (pixelSize) + { + case 32: + if (componentCount != 3 || componentSize != 8) + return 31; + break; + case 16: + if (componentCount != 3 || componentSize != 5) + return 32; + break; + case 8: + if (componentCount != 1 || componentSize != 8) + return 33; + break; + default: + return 34; + } + } + } + else + { + switch (pixMapBE.m_componentSize) + { + case 1: + case 2: + case 4: + case 8: + break; + default: + return 35; + } + + if (componentSize != pixelSize) + return 36; + } + + if (packType > 4) + return 37; + } + + const int componentSize = pixMapBE.m_componentSize; + + // We only support rect moves that are the same size + if (srcRect.right - srcRect.left != destRect.right - destRect.left) + return 38; + + if (srcRect.bottom - srcRect.top != destRect.bottom - destRect.top) + return 39; + + const Rect pixMapBounds = pixMapBE.m_bounds.ToRect(); + if (!pixMapBounds.IsValid()) + return 40; + + if (srcRect.left < pixMapBounds.left || srcRect.right > pixMapBounds.right || srcRect.top < pixMapBounds.top || srcRect.bottom > pixMapBounds.bottom) + return 41; + + const Vec2i pixMapOriginRelativeToSrcRect = Vec2i(pixMapBounds.left, pixMapBounds.top) - Vec2i(srcRect.left, srcRect.top); + const Vec2i pixMapOriginRelativeToDestRect = pixMapOriginRelativeToSrcRect; + + const Vec2i pixMapOrigin = pixMapOriginRelativeToDestRect + Vec2i(destRect.left, destRect.top); + const Vec2i pixMapBottomRight = pixMapOrigin + Vec2i(pixMapBounds.right, pixMapBounds.bottom) - Vec2i(pixMapBounds.left, pixMapBounds.top); + + const Rect constrainedDestRect = constraintRect.Intersect(destRect).MakeValid(); + + bool skipAll = false; + if (!constrainedDestRect.IsValid()) + skipAll = true; + + if (rowSizeBytes < 8) + packType = 1; + + if (!isDirect) + { + if (packType != 0 && packType != 1) + return 42; + } + + // NOT CURRENTLY SUPPORTED + if (packType == 2) + return 43; + + size_t decompressedRowSize = rowSizeBytes; + + const size_t minimumRowSize = (static_cast(pixMapBounds.right - pixMapBounds.left) * static_cast(pixMapBE.m_pixelSize) + 7) / 8; + if (decompressedRowSize < minimumRowSize) + return 56; + + if (packType == 4) + { + if ((rowSizeBytes & 3) != 0) + return 55; + + // Fudge decompressed row size in planar RGB case + decompressedRowSize = (rowSizeBytes / 4) * 3; + } + + // Max compressed size is 4 + const size_t maxCompressedSize = decompressedRowSize + (decompressedRowSize / 128) + 4; + + uint8_t *decompressedScanlineBuffer = nullptr; + uint8_t *compressedScanlineBuffer = nullptr; + + if (!context->AllocTempBuffers(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, maxCompressedSize)) + return 57; + + bool started = false; + + QDPictEmitScanlineParameters params; + params.m_scanlineOriginX = pixMapOrigin.m_x; + params.m_firstY = constrainedDestRect.top; + params.m_constrainedRegionLeft = constrainedDestRect.left; + params.m_constrainedRegionRight = constrainedDestRect.right; + params.m_colors = colors; + params.m_numColors = numColors; + params.m_planarSeparation = pixMapBottomRight.m_x - pixMapOrigin.m_x; + + for (int32_t y = pixMapOrigin.m_y; y < pixMapBottomRight.m_y; y++) + { + bool isLineValid = !skipAll; + if (y < constrainedDestRect.top || y >= constrainedDestRect.bottom) + isLineValid = false; + + if (packType == 0 || packType > 2) + { + // RLE + uint16_t lineByteCount; + if (rowSizeBytes > 250) + { + if (stream->Read(&lineByteCount, 2) != 2) + return 44; + ByteSwap::BigUInt16(lineByteCount); + } + else + { + uint8_t lineByteCountSmall; + if (stream->Read(&lineByteCountSmall, 1) != 1) + return 45; + lineByteCount = lineByteCountSmall; + } + + if (!isLineValid) + { + if (!stream->SeekCurrent(lineByteCount)) + return 46; + continue; + } + + if (lineByteCount > maxCompressedSize) + return 58; + + if (stream->Read(compressedScanlineBuffer, lineByteCount) != lineByteCount) + return 47; + + if (packType == 3) + { + // 16-bit RLE + if (!UnpackBits16(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, lineByteCount)) + return 48; + + if (!started) + { + context->Start(QDPictBlitSourceType_RGB15, params); + started = true; + } + + context->BlitScanlineAndAdvance(decompressedScanlineBuffer); + } + else + { + // 8-bit RLE + if (!UnpackBits8(decompressedScanlineBuffer, decompressedRowSize, compressedScanlineBuffer, lineByteCount)) + return 49; + + if (!started) + { + if (packType == 0) + { + switch (componentSize) + { + case 1: + context->Start(isPixMap ? QDPictBlitSourceType_Indexed1Bit : QDPictBlitSourceType_1Bit, params); + break; + case 2: + context->Start(QDPictBlitSourceType_Indexed2Bit, params); + break; + case 4: + context->Start(QDPictBlitSourceType_Indexed4Bit, params); + break; + case 8: + context->Start(QDPictBlitSourceType_Indexed8Bit, params); + break; + default: + return 50; // ??? + } + } + else + { + assert(packType == 4); + context->Start(QDPictBlitSourceType_RGB24_Multiplane, params); + } + started = true; + } + + context->BlitScanlineAndAdvance(decompressedScanlineBuffer); + } + } + else if (packType == 1) + { + if (stream->Read(decompressedScanlineBuffer, rowSizeBytes) != rowSizeBytes) + return 51; + + if (!started) + { + switch (componentSize) + { + case 1: + context->Start(isPixMap ? QDPictBlitSourceType_Indexed1Bit : QDPictBlitSourceType_1Bit, params); + break; + case 2: + context->Start(QDPictBlitSourceType_Indexed2Bit, params); + break; + case 4: + context->Start(QDPictBlitSourceType_Indexed4Bit, params); + break; + case 8: + context->Start(QDPictBlitSourceType_Indexed8Bit, params); + break; + default: + return 52; // ??? + } + + started = true; + } + + context->BlitScanlineAndAdvance(decompressedScanlineBuffer); + } + else + return 53; + } + + // Either undocumented behavior or non-compliant PICT resources, not sure + if (isPixMap && (stream->Tell() & 1) != 0) + { + if (!stream->SeekCurrent(1)) + return 54; + } + + return 0; + } + + bool QDPictDecoder::UnpackBits8(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize) + { + while (srcSize > 0) + { + int8_t headerByte = *reinterpret_cast(src); + src++; + srcSize--; + + if (headerByte >= 0) + { + const size_t litCount = headerByte + 1; + if (destSize < litCount || srcSize < litCount) + return false; + + memcpy(dest, src, litCount); + + src += litCount; + srcSize -= litCount; + dest += litCount; + destSize -= litCount; + } + else + { + const size_t repCount = static_cast(1 - headerByte); + if (srcSize < 1) + return false; + + if (destSize < repCount) + return false; + + const uint8_t repValue = *src; + + memset(dest, repValue, repCount); + + src++; + srcSize--; + dest += repCount; + destSize -= repCount; + } + } + + return (destSize == 0); + } + + bool QDPictDecoder::UnpackBits16(uint8_t *dest8, size_t destSize, const uint8_t *src, size_t srcSize) + { + uint16_t *dest16 = reinterpret_cast(dest8); + destSize /= 2; + + while (srcSize > 0) + { + int8_t headerByte = *reinterpret_cast(src); + src++; + srcSize--; + + if (headerByte >= 0) + { + const size_t litCount = headerByte + 1; + if (destSize < litCount || srcSize < litCount * 2) + return false; + + memcpy(dest16, src, 2 * litCount); + for (size_t i = 0; i < litCount; i++) + ByteSwap::BigUInt16(dest16[i]); + + src += litCount * 2; + srcSize -= litCount * 2; + dest16 += litCount; + destSize -= litCount; + } + else + { + const size_t repCount = static_cast(1 - headerByte); + if (srcSize < 2) + return false; + + if (destSize < repCount) + return false; + + const uint16_t repValue = (src[0] << 8) | src[1]; + + for (size_t i = 0; i < repCount; i++) + dest16[i] = repValue; + + src += 2; + srcSize -= 2; + dest16 += repCount; + destSize -= repCount; + } + } + + return (destSize == 0); + } +} diff --git a/PortabilityLayer/QDPictDecoder.h b/PortabilityLayer/QDPictDecoder.h new file mode 100644 index 0000000..cc71375 --- /dev/null +++ b/PortabilityLayer/QDPictDecoder.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +struct Rect; + +namespace PortabilityLayer +{ + class IOStream; + class QDPictEmitContext; + + class QDPictDecoder + { + public: + QDPictDecoder(); + + bool DecodePict(IOStream *stream, QDPictEmitContext *emitContext); + + private: + int ProcessRasterOp(IOStream *stream, int pictVersion, bool isPackedFlag, bool hasRegion, bool isDirect, const Rect &drawArea, QDPictEmitContext *context); + static bool UnpackBits8(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize); + static bool UnpackBits16(uint8_t *dest, size_t destSize, const uint8_t *src, size_t srcSize); + + IOStream *m_stream; + }; +} diff --git a/PortabilityLayer/QDPictEmitContext.cpp b/PortabilityLayer/QDPictEmitContext.cpp new file mode 100644 index 0000000..810b8f7 --- /dev/null +++ b/PortabilityLayer/QDPictEmitContext.cpp @@ -0,0 +1,18 @@ +#include "QDPictEmitContext.h" + +namespace PortabilityLayer +{ + bool QDPictBlitSourceType_IsIndexed(QDPictBlitSourceType sourceType) + { + switch (sourceType) + { + case QDPictBlitSourceType_Indexed1Bit: + case QDPictBlitSourceType_Indexed2Bit: + case QDPictBlitSourceType_Indexed4Bit: + case QDPictBlitSourceType_Indexed8Bit: + return true; + default: + return false; + } + } +} diff --git a/PortabilityLayer/QDPictEmitContext.h b/PortabilityLayer/QDPictEmitContext.h new file mode 100644 index 0000000..cbdc87b --- /dev/null +++ b/PortabilityLayer/QDPictEmitContext.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +struct Rect; + +namespace PortabilityLayer +{ + struct RGBAColor; + struct QDPictEmitScanlineParameters; + + enum QDPictBlitSourceType + { + QDPictBlitSourceType_1Bit, + QDPictBlitSourceType_Indexed1Bit, + QDPictBlitSourceType_Indexed2Bit, + QDPictBlitSourceType_Indexed4Bit, + QDPictBlitSourceType_Indexed8Bit, + QDPictBlitSourceType_RGB15, + QDPictBlitSourceType_RGB24_Interleaved, + QDPictBlitSourceType_RGB24_Multiplane, + }; + + bool QDPictBlitSourceType_IsIndexed(QDPictBlitSourceType sourceType); + + class QDPictEmitContext + { + public: + virtual bool SpecifyFrame(const Rect &rect) = 0; + virtual Rect ConstrainRegion(const Rect &rect) const = 0; + virtual void Start(QDPictBlitSourceType sourceType, const QDPictEmitScanlineParameters ¶ms) = 0; + virtual void BlitScanlineAndAdvance(const void *) = 0; + virtual bool AllocTempBuffers(uint8_t *&buffer1, size_t buffer1Size, uint8_t *&buffer2, size_t buffer2Size) = 0; + }; +} \ No newline at end of file diff --git a/PortabilityLayer/QDPictEmitScanlineParameters.h b/PortabilityLayer/QDPictEmitScanlineParameters.h new file mode 100644 index 0000000..a96bc7c --- /dev/null +++ b/PortabilityLayer/QDPictEmitScanlineParameters.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + struct RGBAColor; + + struct QDPictEmitScanlineParameters + { + const RGBAColor *m_colors; + size_t m_numColors; + size_t m_planarSeparation; + + int32_t m_scanlineOriginX; + int32_t m_firstY; + int32_t m_constrainedRegionLeft; + int32_t m_constrainedRegionRight; + }; +} diff --git a/PortabilityLayer/QDPictHeader.cpp b/PortabilityLayer/QDPictHeader.cpp new file mode 100644 index 0000000..9738adb --- /dev/null +++ b/PortabilityLayer/QDPictHeader.cpp @@ -0,0 +1,70 @@ +#include "QDPictHeader.h" +#include "IOStream.h" +#include "SharedTypes.h" +#include "CoreDefs.h" + +#include +#include + +namespace PortabilityLayer +{ + QDPictHeader::QDPictHeader() + : m_pictVersion(0) + { + } + + bool QDPictHeader::Load(IOStream *stream) + { + PL_STATIC_ASSERT(sizeof(Picture) == 10); + + Picture pictHeader; + if (stream->Read(&pictHeader, sizeof(Picture)) != sizeof(Picture)) + return false; + + m_frame = pictHeader.picFrame.ToRect(); + if (!m_frame.IsValid()) + return false; + + uint8_t versionTag1[2]; + if (stream->Read(versionTag1, 2) != 2) + return false; + + if (versionTag1[0] == 0x11 && versionTag1[1] == 0x01) + { + m_pictVersion = 1; + } + else if (versionTag1[0] == 0x00 && versionTag1[1] == 0x11) + { + m_pictVersion = 2; + + uint8_t v2Header[28]; + if (stream->Read(v2Header, 28) != 28) + return false; + + if (v2Header[0] != 0x02 || v2Header[1] != 0xff || v2Header[2] != 0x0c || v2Header[3] != 0x00) + return false; + + BEInt16_t v2Version; + memcpy(&v2Version, v2Header + 4, 2); + + // In version 2 header, v2Version == -1 + // Followed by fixed-point bounding rectangle (16 bytes) and 4 reserved + // In ext. version 2 header, v2Version == -2 + // Followed by 2-byte reserved, horizontal DPI (fixed point, 4 bytes), vertical DPI (fixed point, 4 bytes) optimal source rect (8 bytes), and 2 reserved + } + else + return false; + + return true; + } + + int QDPictHeader::GetVersion() const + { + return m_pictVersion; + } + + const Rect &QDPictHeader::GetFrame() const + { + return m_frame; + } +} \ No newline at end of file diff --git a/PortabilityLayer/QDPictHeader.h b/PortabilityLayer/QDPictHeader.h new file mode 100644 index 0000000..7c376dc --- /dev/null +++ b/PortabilityLayer/QDPictHeader.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "SharedTypes.h" + +namespace PortabilityLayer +{ + class IOStream; + + class QDPictHeader + { + public: + QDPictHeader(); + bool Load(IOStream *stream); + + int GetVersion() const; + const Rect &GetFrame() const; + + private: + Rect m_frame; + + int m_pictVersion; + }; +} diff --git a/PortabilityLayer/QDPictOpcodeDefs.h b/PortabilityLayer/QDPictOpcodeDefs.h new file mode 100644 index 0000000..6650228 --- /dev/null +++ b/PortabilityLayer/QDPictOpcodeDefs.h @@ -0,0 +1,18 @@ +PL_PICTOP(0x0000, kNoop, 0) +PL_PICTOP(0x0001, kClipRegion, Rule_SizeTagged16) +PL_PICTOP(0x0002, kBackPattern, 8) +PL_PICTOP(0x0003, kTextFont, 2) +PL_PICTOP(0x0004, kTextFontStyle, 1) +PL_PICTOP(0x0005, kTextSourceMode, 2) +PL_PICTOP(0x001e, kDefaultHilite, 0) +PL_PICTOP(0x001f, kOpColor, 6) +PL_PICTOP(0x0030, kFrameRect, 8) +PL_PICTOP(0x00a0, kShortComment, 2) +PL_PICTOP(0x00a1, kLongComment, Rule_LongComment) +PL_PICTOP(0x0090, kBitsRect, Rule_BitsRect) +PL_PICTOP(0x0091, kBitsRgn, Rule_BitsRgn) +PL_PICTOP(0x0098, kPackBitsRect, Rule_PackBitsRect) +PL_PICTOP(0x0099, kPackBitsRgn, Rule_PackBitsRgn) +PL_PICTOP(0x009a, kDirectBitsRect, Rule_DirectBitsRect) +PL_PICTOP(0x009b, kDirectBitsRgn, Rule_DirectBitsRgn) +PL_PICTOP(0x00ff, kEndOfPicture, 0) diff --git a/PortabilityLayer/QDPictOpcodes.h b/PortabilityLayer/QDPictOpcodes.h new file mode 100644 index 0000000..7383d38 --- /dev/null +++ b/PortabilityLayer/QDPictOpcodes.h @@ -0,0 +1,14 @@ +#pragma once + +namespace PortabilityLayer +{ + namespace QDOpcodes + { + enum Value + { +#define PL_PICTOP(number, opcode, sizeRule) opcode = number, +#include "QDPictOpcodeDefs.h" +#undef PL_PICTOP + }; + } +} diff --git a/PortabilityLayer/QDPixMap.cpp b/PortabilityLayer/QDPixMap.cpp new file mode 100644 index 0000000..ae99a8b --- /dev/null +++ b/PortabilityLayer/QDPixMap.cpp @@ -0,0 +1,68 @@ +#include "QDPixMap.h" +#include "CoreDefs.h" + +#include + +namespace PortabilityLayer +{ + PixMapImpl::PixMapImpl(int16_t left, int16_t top, uint16_t width, uint16_t height, PixelFormat pixelFormat) + : m_left(left) + , m_top(top) + , m_width(width) + , m_height(height) + , m_pitch(PitchForWidth(width, pixelFormat)) + , m_pixelFormat(pixelFormat) + { + m_rect.left = left; + m_rect.top = top; + m_rect.right = static_cast(left + width); + m_rect.bottom = static_cast(top + height); + + m_pitch = (width + PL_SYSTEM_MEMORY_ALIGNMENT - 1); + m_pitch -= m_pitch % PL_SYSTEM_MEMORY_ALIGNMENT; + + m_data = reinterpret_cast(this) + AlignedSize(); + } + + size_t PixMapImpl::SizeForDimensions(uint16_t width, uint16_t height, PixelFormat pixelFormat) + { + return AlignedSize() + PitchForWidth(width, pixelFormat) * height; + } + + size_t PixMapImpl::AlignedSize() + { + const size_t szBase = sizeof(PixMapImpl) + PL_SYSTEM_MEMORY_ALIGNMENT - 1; + const size_t szAdjusted = szBase - szBase % PL_SYSTEM_MEMORY_ALIGNMENT; + + return szAdjusted; + } + + size_t PixMapImpl::PitchForWidth(uint16_t width, PixelFormat pixelFormat) + { + size_t rowByteCount = 0; + + switch (pixelFormat) + { + case PixelFormat_8BitCustom: + case PixelFormat_8BitStandard: + rowByteCount = width; + break; + case PixelFormat_RGB555: + rowByteCount = width * 2; + break; + case PixelFormat_RGB24: + rowByteCount = width * 3; + break; + case PixelFormat_RGB32: + rowByteCount = width * 4; + break; + default: + assert(false); + return 0; + } + const size_t szBase = rowByteCount + PL_SYSTEM_MEMORY_ALIGNMENT - 1; + const size_t szAdjusted = szBase - szBase % PL_SYSTEM_MEMORY_ALIGNMENT; + + return szAdjusted; + } +} diff --git a/PortabilityLayer/QDPixMap.h b/PortabilityLayer/QDPixMap.h new file mode 100644 index 0000000..385a167 --- /dev/null +++ b/PortabilityLayer/QDPixMap.h @@ -0,0 +1,60 @@ +#pragma once + +#include "PLBigEndian.h" +#include "PLQuickdraw.h" +#include "PixelFormat.h" + +#include + +struct PixMap : public BitMap +{ +}; + +namespace PortabilityLayer +{ + class PixMapImpl final : public PixMap + { + public: + PixMapImpl(int16_t left, int16_t top, uint16_t width, uint16_t height, PixelFormat pixelFormat); + + PixelFormat GetPixelFormat() const; + size_t GetPitch() const; + void *GetPixelData(); + const void *GetPixelData() const; + + static size_t SizeForDimensions(uint16_t width, uint16_t height, PixelFormat pixelFormat); + + private: + static size_t AlignedSize(); + static size_t PitchForWidth(uint16_t width, PixelFormat pixelFormat); + + int16_t m_left; + int16_t m_top; + uint16_t m_width; + uint16_t m_height; + PixelFormat m_pixelFormat; + + size_t m_pitch; + void *m_data; + }; +} + +inline PortabilityLayer::PixelFormat PortabilityLayer::PixMapImpl::GetPixelFormat() const +{ + return m_pixelFormat; +} + +inline size_t PortabilityLayer::PixMapImpl::GetPitch() const +{ + return m_pitch; +} + +inline void *PortabilityLayer::PixMapImpl::GetPixelData() +{ + return m_data; +} + +inline const void *PortabilityLayer::PixMapImpl::GetPixelData() const +{ + return m_data; +} diff --git a/PortabilityLayer/QDPort.cpp b/PortabilityLayer/QDPort.cpp new file mode 100644 index 0000000..9c2738f --- /dev/null +++ b/PortabilityLayer/QDPort.cpp @@ -0,0 +1,77 @@ +#include "QDPort.h" +#include "PLErrorCodes.h" +#include "MemoryManager.h" +#include "MMHandleBlock.h" +#include "QDPixMap.h" + +namespace PortabilityLayer +{ + QDPort::QDPort(QDPortType portType) + : m_portType(portType) + , m_left(0) + , m_top(0) + , m_width(0) + , m_height(0) + , m_pixMap(nullptr) + , m_pixelFormat(PixelFormat_Invalid) + { + } + + QDPort::~QDPort() + { + if (m_pixMap) + { + if (*m_pixMap) + static_cast(*m_pixMap)->~PixMapImpl(); + MemoryManager::GetInstance()->ReleaseHandle(reinterpret_cast(m_pixMap)); + } + } + + int QDPort::Init(const Rect &rect, PixelFormat pixelFormat) + { + m_left = rect.left; + m_top = rect.top; + m_width = static_cast(rect.right - rect.left); + m_height = static_cast(rect.bottom - rect.top); + m_pixMap = nullptr; + m_pixelFormat = pixelFormat; + + size_t pixMapSize = PixMapImpl::SizeForDimensions(m_width, m_height, m_pixelFormat); + if (pixMapSize == 0) + return mFulErr; + + MMHandleBlock *pmBlock = MemoryManager::GetInstance()->AllocHandle(pixMapSize); + if (!pmBlock) + return mFulErr; + + new (pmBlock->m_contents) PixMapImpl(m_left, m_top, m_width, m_height, m_pixelFormat); + m_pixMap = reinterpret_cast(&pmBlock->m_contents); + + return noErr; + } + + PixMap **QDPort::GetPixMap() const + { + return m_pixMap; + } + + const QDState *QDPort::GetState() const + { + return &m_state; + } + + QDState *QDPort::GetState() + { + return &m_state; + } + + PixelFormat QDPort::GetPixelFormat() const + { + return m_pixelFormat; + } + + Rect QDPort::GetRect() const + { + return Rect::Create(0, 0, m_height, m_width); + } +} diff --git a/PortabilityLayer/QDPort.h b/PortabilityLayer/QDPort.h new file mode 100644 index 0000000..e32cdb5 --- /dev/null +++ b/PortabilityLayer/QDPort.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include "PixelFormat.h" +#include "QDState.h" + +struct PixMap; +struct Rect; + +namespace PortabilityLayer +{ + enum QDPortType + { + QDPortType_Invalid, + + QDPortType_CGraf, + QDPortType_Window, + }; + + class QDPort + { + public: + explicit QDPort(QDPortType portType); + ~QDPort(); + + int Init(const Rect &rect, PixelFormat pixelFormat); + QDPortType GetPortType() const; + + PixMap **GetPixMap() const; + const QDState *GetState() const; + QDState *GetState(); + PixelFormat GetPixelFormat() const; + Rect GetRect() const; + + private: + QDPortType m_portType; + + QDState m_state; + PixMap **m_pixMap; + + int16_t m_left; + int16_t m_top; + uint16_t m_width; + uint16_t m_height; + PixelFormat m_pixelFormat; + }; + + inline QDPortType QDPort::GetPortType() const + { + return m_portType; + } +} diff --git a/PortabilityLayer/QDRegion.h b/PortabilityLayer/QDRegion.h new file mode 100644 index 0000000..c884c94 --- /dev/null +++ b/PortabilityLayer/QDRegion.h @@ -0,0 +1,5 @@ +#pragma once + +#include "ByteSwap.h" +#include "SharedTypes.h" + diff --git a/PortabilityLayer/QDStandardPalette.cpp b/PortabilityLayer/QDStandardPalette.cpp new file mode 100644 index 0000000..82ce866 --- /dev/null +++ b/PortabilityLayer/QDStandardPalette.cpp @@ -0,0 +1,228 @@ +#include "QDStandardPalette.h" + +#include + +// Standard palette mapping is 6x6x6, with the last element omitted for an RGB table +// which ranges from 0..214 +// 10 elements of red scale from 215..224 +// 10 elements of green scale from 225..234 +// 10 elements of blue scale from 235..244 +// 10 elements of gray scale from 245..254 +// black at 255 + +namespace +{ + static void DivMod15By5RoundDown(unsigned int value, unsigned int &valueDiv3Out, unsigned int &valueMod3Out) + { + const unsigned int valueDiv3 = (value * 11) >> 5; + const unsigned int valueMod3 = value - valueDiv3 * 3; + valueDiv3Out = valueDiv3; + valueMod3Out = valueMod3; + } +} + +namespace PortabilityLayer +{ + StandardPalette::StandardPalette() + { + for (unsigned int rs = 0; rs < 6; rs++) + { + for (unsigned int gs = 0; gs < 6; gs++) + { + for (unsigned int bs = 0; bs < 6; bs++) + { + RGBAColor &entry = m_colors[215 - bs - gs * 6 - rs * 36]; + entry.r = rs * 51; + entry.g = gs * 51; + entry.b = bs * 51; + entry.a = 255; + } + } + } + + for (unsigned int scale = 0; scale < 5; scale++) + { + for (unsigned int tickUp = 0; tickUp < 2; tickUp++) + { + const unsigned int scaleValue = (scale * 3 + tickUp + 1) * 17; + const unsigned int scaleIndex = scale * 2 + tickUp; + + RGBAColor &rScaleEntry = m_colors[224 - scaleIndex]; + RGBAColor &gScaleEntry = m_colors[234 - scaleIndex]; + RGBAColor &bScaleEntry = m_colors[244 - scaleIndex]; + RGBAColor &grayScaleEntry = m_colors[254 - scaleIndex]; + + rScaleEntry.r = scaleValue; + rScaleEntry.g = 0; + rScaleEntry.b = 0; + rScaleEntry.a = 255; + + gScaleEntry.r = 0; + gScaleEntry.g = scaleValue; + gScaleEntry.b = 0; + gScaleEntry.a = 255; + + bScaleEntry.r = 0; + bScaleEntry.g = 0; + bScaleEntry.b = scaleValue; + bScaleEntry.a = 255; + + grayScaleEntry.r = scaleValue; + grayScaleEntry.g = scaleValue; + grayScaleEntry.b = scaleValue; + grayScaleEntry.a = 255; + } + } + + RGBAColor &blackEntry = m_colors[255]; + blackEntry.r = blackEntry.g = blackEntry.b = 0; + blackEntry.a = 255; + + for (unsigned int rs = 0; rs < 16; rs++) + for (unsigned int gs = 0; gs < 16; gs++) + for (unsigned int bs = 0; bs < 16; bs++) + m_lut[rs + (gs << 4) + (bs << 8)] = MapColorAnalyticTruncated(rs, gs, bs); + } + + const RGBAColor *StandardPalette::GetColors() const + { + return m_colors; + } + + uint8_t StandardPalette::MapColorAnalyticTruncated(unsigned int r, unsigned int g, unsigned int b) + { + if (g <= 1 && g <= 1) + { + // Red scale + unsigned int scale6Step; + unsigned int scale6StepRemainder; + DivMod15By5RoundDown(r, scale6Step, scale6StepRemainder); + + if (scale6StepRemainder == 0) + { + if (scale6Step == 0) + return 255; + else + return 215 - scale6Step * 36; + } + else + return 225 - scale6Step * 2 - scale6StepRemainder; + } + + if (r <= 1 && b <= 1) + { + // Green scale + unsigned int scale6Step; + unsigned int scale6StepRemainder; + DivMod15By5RoundDown(g, scale6Step, scale6StepRemainder); + + if (scale6StepRemainder == 0) + { + if (scale6Step == 0) + return 255; + else + return 215 - scale6Step * 6; + } + else + return 235 - scale6Step * 2 - scale6StepRemainder; + } + + if (r <= 1 && g <= 1) + { + // Blue scale + unsigned int scale6Step; + unsigned int scale6StepRemainder; + DivMod15By5RoundDown(b, scale6Step, scale6StepRemainder); + + if (scale6StepRemainder == 0) + { + if (scale6Step == 0) + return 255; + else + return 215 - scale6Step; + } + else + return 245 - scale6Step * 2 - scale6StepRemainder; + } + + const unsigned int grayscaleTimes3 = r + g + b; + const int rGrayDelta = static_cast(r * 3) - static_cast(grayscaleTimes3); + const int gGrayDelta = static_cast(g * 3) - static_cast(grayscaleTimes3); + const int bGrayDelta = static_cast(b * 3) - static_cast(grayscaleTimes3); + + if (rGrayDelta >= -3 && rGrayDelta <= 3 && gGrayDelta >= -3 && gGrayDelta <= -3 && bGrayDelta >= -3 && bGrayDelta <= -3) + { + // Divide down to 0..15 range + const unsigned int grayscaleValue = (grayscaleTimes3 * 21 + 36) >> 6; + + // Divide down again to 0..5 range, rounding down + unsigned int grayscale6Step; + unsigned int grayscale6StepRemainder; + DivMod15By5RoundDown(grayscaleValue, grayscale6Step, grayscale6StepRemainder); + + if (grayscale6StepRemainder == 0) + { + if (grayscale6Step == 0) + return 255; + else if (grayscale6Step == 5) + return 0; + else + return 180 - 36 * grayscale6Step; + } + else + return 255 - grayscale6Step * 2 - grayscale6StepRemainder; + } + + // Round to 0..5 range + const unsigned int rSmallNearest6 = (r * 11 + 11) >> 5; + const unsigned int gSmallNearest6 = (g * 11 + 11) >> 5; + const unsigned int bSmallNearest6 = (b * 11 + 11) >> 5; + + if ((rSmallNearest6 | gSmallNearest6 | bSmallNearest6) == 0) + return 255; + else + return 215 - bSmallNearest6 - gSmallNearest6 * 6 - rSmallNearest6 * 36; + } + + uint8_t StandardPalette::MapColorAnalytic(uint8_t r, uint8_t g, uint8_t b) + { + // Round to 0..15 range + const unsigned int rSmall = (r * 241 + 2048) >> 12; + const unsigned int gSmall = (g * 241 + 2048) >> 12; + const unsigned int bSmall = (b * 241 + 2048) >> 12; + + return MapColorAnalyticTruncated(rSmall, gSmall, bSmall); + } + + uint8_t StandardPalette::MapColorAnalytic(const RGBAColor &color) + { + // Round to 0..15 range + const unsigned int rSmall = (color.r * 241 + 2048) >> 12; + const unsigned int gSmall = (color.g * 241 + 2048) >> 12; + const unsigned int bSmall = (color.b * 241 + 2048) >> 12; + + return MapColorAnalyticTruncated(rSmall, gSmall, bSmall); + } + + uint8_t StandardPalette::MapColorLUT(uint8_t r, uint8_t g, uint8_t b) const + { + // Round to 0..15 range + const unsigned int rSmall = (r * 241 + 2048) >> 12; + const unsigned int gSmall = (g * 241 + 2048) >> 12; + const unsigned int bSmall = (b * 241 + 2048) >> 12; + + return m_lut[rSmall + (gSmall << 4) + (bSmall << 8)]; + } + + uint8_t StandardPalette::MapColorLUT(const RGBAColor &color) const + { + return MapColorLUT(color.r, color.g, color.b); + } + + const StandardPalette *StandardPalette::GetInstance() + { + return &ms_instance; + } + + StandardPalette StandardPalette::ms_instance; +} diff --git a/PortabilityLayer/QDStandardPalette.h b/PortabilityLayer/QDStandardPalette.h new file mode 100644 index 0000000..06be3c6 --- /dev/null +++ b/PortabilityLayer/QDStandardPalette.h @@ -0,0 +1,29 @@ +#pragma once + +#include "RGBAColor.h" + +namespace PortabilityLayer +{ + class StandardPalette + { + public: + static const unsigned int kSize = 256; + + StandardPalette(); + + const RGBAColor *GetColors() const; + static uint8_t MapColorAnalyticTruncated(unsigned int r, unsigned int g, unsigned int b); + static uint8_t MapColorAnalytic(uint8_t r, uint8_t g, uint8_t b); + static uint8_t MapColorAnalytic(const RGBAColor &color); + uint8_t MapColorLUT(uint8_t r, uint8_t g, uint8_t b) const; + uint8_t MapColorLUT(const RGBAColor &color) const; + + static const StandardPalette *GetInstance(); + + private: + static StandardPalette ms_instance; + + RGBAColor m_colors[kSize]; + uint8_t m_lut[16 * 16 * 16]; + }; +} diff --git a/PortabilityLayer/QDState.cpp b/PortabilityLayer/QDState.cpp new file mode 100644 index 0000000..c6bbace --- /dev/null +++ b/PortabilityLayer/QDState.cpp @@ -0,0 +1,71 @@ +#include "QDState.h" + +#include "PLQuickdraw.h" +#include "QDStandardPalette.h" + +namespace PortabilityLayer +{ + QDState::QDState() + : m_fontID(applFont) + , m_textSize(12) + , m_textFace(0) + , m_foreResolvedColor16(0) + , m_backResolvedColor16(0) + , m_foreResolvedColor8(0) + , m_backResolvedColor8(0) + , m_isForeResolved16(false) + , m_isBackResolved16(false) + , m_isForeResolved8(false) + , m_isBackResolved8(false) + , m_clipRegion(nullptr) + { + m_backUnresolvedColor.r = m_backUnresolvedColor.g = m_backUnresolvedColor.b = m_backUnresolvedColor.a = 255; + m_foreUnresolvedColor.r = m_foreUnresolvedColor.g = m_foreUnresolvedColor.b = 0; + m_foreUnresolvedColor.a = 255; + } + + void QDState::SetForeColor(const RGBAColor &color) + { + m_foreUnresolvedColor = color; + m_isForeResolved16 = false; + m_isForeResolved8 = false; + } + + void QDState::SetBackColor(const RGBAColor &color) + { + m_backUnresolvedColor = color; + m_isBackResolved16 = false; + m_isBackResolved8 = false; + } + + uint8_t QDState::ResolveForeColor8(const RGBAColor *palette, unsigned int numColors) + { + return ResolveColor8(m_foreUnresolvedColor, m_foreResolvedColor8, m_isForeResolved8, palette, numColors); + } + + uint8_t QDState::ResolveBackColor8(const RGBAColor *palette, unsigned int numColors) + { + return ResolveColor8(m_backUnresolvedColor, m_backResolvedColor8, m_isBackResolved8, palette, numColors); + } + + uint8_t QDState::ResolveColor8(const RGBAColor &color, uint8_t &cached, bool &isCached, const RGBAColor *palette, unsigned int numColors) + { + if (isCached) + return cached; + + if (palette) + { + PL_NotYetImplemented(); + return 0; + } + else + { + const uint8_t resolvedColor = StandardPalette::GetInstance()->MapColorLUT(color); + + isCached = true; + cached = resolvedColor; + + return resolvedColor; + } + } +} diff --git a/PortabilityLayer/QDState.h b/PortabilityLayer/QDState.h new file mode 100644 index 0000000..dd47a55 --- /dev/null +++ b/PortabilityLayer/QDState.h @@ -0,0 +1,40 @@ +#pragma once + +#include "RGBAColor.h" + +struct Region; + +namespace PortabilityLayer +{ + struct QDState + { + QDState(); + + int m_fontID; + int m_textFace; + int m_textSize; + Region **m_clipRegion; + + void SetForeColor(const RGBAColor &color); + void SetBackColor(const RGBAColor &color); + + uint8_t ResolveForeColor8(const RGBAColor *palette, unsigned int numColors); + uint8_t ResolveBackColor8(const RGBAColor *palette, unsigned int numColors); + + private: + static uint8_t ResolveColor8(const RGBAColor &color, uint8_t &cached, bool &isCached, const RGBAColor *palette, unsigned int numColors); + + RGBAColor m_foreUnresolvedColor; + RGBAColor m_backUnresolvedColor; + + uint16_t m_foreResolvedColor16; + uint16_t m_backResolvedColor16; + uint8_t m_foreResolvedColor8; + uint8_t m_backResolvedColor8; + + bool m_isForeResolved16; + bool m_isForeResolved8; + bool m_isBackResolved16; + bool m_isBackResolved8; + }; +} diff --git a/PortabilityLayer/QDUtils.cpp b/PortabilityLayer/QDUtils.cpp new file mode 100644 index 0000000..b2362c1 --- /dev/null +++ b/PortabilityLayer/QDUtils.cpp @@ -0,0 +1,31 @@ +#include "QDUtils.h" +#include "MemoryManager.h" +#include "SharedTypes.h" + +namespace PortabilityLayer +{ + Region **QDUtils::CreateRegion(const Rect &rect) + { + PL_STATIC_ASSERT(sizeof(Region) == 10); + + Region **rgnHandle = MemoryManager::GetInstance()->NewHandle(); + if (!rgnHandle) + return nullptr; + + Region ®ion = (**rgnHandle); + region.rect = rect; + region.size = sizeof(Region); + + return rgnHandle; + } + + void QDUtils::ResetRegionToRect(Region **regionHdl, const Rect &rect) + { + if (MemoryManager::GetInstance()->ResizeHandle(reinterpret_cast(regionHdl), sizeof(Region))) + { + Region ®ion = **regionHdl; + region.size = sizeof(Region); + region.rect = rect; + } + } +} diff --git a/PortabilityLayer/QDUtils.h b/PortabilityLayer/QDUtils.h new file mode 100644 index 0000000..bd73f07 --- /dev/null +++ b/PortabilityLayer/QDUtils.h @@ -0,0 +1,14 @@ +#pragma once + +struct Region; +struct Rect; + +namespace PortabilityLayer +{ + class QDUtils + { + public: + static Region **CreateRegion(const Rect &rect); + static void ResetRegionToRect(Region **region, const Rect &rect); + }; +} diff --git a/PortabilityLayer/RGBAColor.h b/PortabilityLayer/RGBAColor.h new file mode 100644 index 0000000..6aaf6f9 --- /dev/null +++ b/PortabilityLayer/RGBAColor.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + struct RGBAColor + { + uint8_t r, g, b, a; + }; +} diff --git a/PortabilityLayer/RandomNumberGenerator.cpp b/PortabilityLayer/RandomNumberGenerator.cpp index 1baf819..0dd470a 100644 --- a/PortabilityLayer/RandomNumberGenerator.cpp +++ b/PortabilityLayer/RandomNumberGenerator.cpp @@ -18,7 +18,7 @@ namespace PortabilityLayer static const int kN = 624; static const int kM = 397; static const int kR = 31; - static const uint32_t kLowMask = (1 << kR) - 1; + static const uint32_t kLowMask = (static_cast(1) << kR) - 1; static const uint32_t kHighMask = ~kLowMask; void Twist(); diff --git a/PortabilityLayer/Rect2i.h b/PortabilityLayer/Rect2i.h new file mode 100644 index 0000000..497e2ad --- /dev/null +++ b/PortabilityLayer/Rect2i.h @@ -0,0 +1,165 @@ +#pragma once + +#include + +#include "Vec2i.h" + +struct Rect; + +namespace PortabilityLayer +{ + struct Vec2i; + + struct Rect2i + { + Vec2i m_topLeft; + Vec2i m_bottomRight; + + Rect2i(); + Rect2i(const Rect2i &other); + explicit Rect2i(const Rect &other); + Rect2i(const Vec2i &topLeft, const Vec2i &bottomRight); + Rect2i(int32_t left, int32_t top, int32_t bottom, int32_t right); + + Rect2i operator+(const Vec2i &other) const; + Rect2i operator-(const Vec2i &other) const; + + Rect2i &operator+=(const Vec2i &other); + Rect2i &operator-=(const Vec2i &other); + + int32_t Top() const; + void SetTop(int32_t i); + + int32_t Left() const; + void SetLeft(int32_t i); + + int32_t Bottom() const; + void SetBottom(int32_t i); + + int32_t Right() const; + void SetRight(int32_t i); + + bool IsValid() const; + Rect2i Intersect(const Rect2i &other) const; + + Rect ToShortRect() const; + }; +} + +#include "SharedTypes.h" +#include + +namespace PortabilityLayer +{ + inline Rect2i::Rect2i() + { + } + + inline Rect2i::Rect2i(const Rect2i &other) + : m_topLeft(other.m_topLeft) + , m_bottomRight(other.m_bottomRight) + { + } + + inline Rect2i::Rect2i(const Rect &other) + : m_topLeft(other.left, other.top) + , m_bottomRight(other.right, other.bottom) + { + } + + inline Rect2i::Rect2i(int32_t top, int32_t left, int32_t bottom, int32_t right) + : m_topLeft(left, top) + , m_bottomRight(right, bottom) + { + } + + inline Rect2i::Rect2i(const Vec2i &topLeft, const Vec2i &bottomRight) + : m_topLeft(topLeft) + , m_bottomRight(bottomRight) + { + } + + inline Rect2i Rect2i::operator+(const Vec2i &other) const + { + return Rect2i(m_topLeft + other, m_bottomRight + other); + } + + inline Rect2i Rect2i::operator-(const Vec2i &other) const + { + return Rect2i(m_topLeft - other, m_bottomRight - other); + } + + inline Rect2i &Rect2i::operator+=(const Vec2i &other) + { + m_topLeft += other; + m_bottomRight += other; + return *this; + } + + inline Rect2i &Rect2i::operator-=(const Vec2i &other) + { + m_topLeft -= other; + m_bottomRight -= other; + return *this; + } + + inline int32_t Rect2i::Top() const + { + return m_topLeft.m_y; + } + + inline void Rect2i::SetTop(int32_t i) + { + m_topLeft.m_y = i; + } + + inline int32_t Rect2i::Left() const + { + return m_topLeft.m_x; + } + + inline void Rect2i::SetLeft(int32_t i) + { + m_topLeft.m_x = i; + } + + inline int32_t Rect2i::Bottom() const + { + return m_bottomRight.m_y; + } + + inline void Rect2i::SetBottom(int32_t i) + { + m_bottomRight.m_y = i; + } + + inline int32_t Rect2i::Right() const + { + return m_bottomRight.m_x; + } + + inline void Rect2i::SetRight(int32_t i) + { + m_bottomRight.m_x = i; + } + + bool Rect2i::IsValid() const + { + return m_bottomRight.m_x >= m_topLeft.m_x && m_bottomRight.m_y >= m_topLeft.m_y; + } + + Rect2i Rect2i::Intersect(const Rect2i &other) const + { + const int32_t top = std::max(m_topLeft.m_y, other.m_topLeft.m_y); + const int32_t left = std::max(m_topLeft.m_x, other.m_topLeft.m_x); + const int32_t bottom = std::min(m_bottomRight.m_y, other.m_bottomRight.m_y); + const int32_t right = std::min(m_bottomRight.m_x, other.m_bottomRight.m_x); + + return Rect2i(top, left, bottom, right); + } + + Rect Rect2i::ToShortRect() const + { + return Rect::Create(static_cast(m_topLeft.m_y), static_cast(m_topLeft.m_x), static_cast(m_bottomRight.m_y), static_cast(m_bottomRight.m_x)); + } +} diff --git a/PortabilityLayer/ResourceCompiledTypeList.h b/PortabilityLayer/ResourceCompiledTypeList.h new file mode 100644 index 0000000..084f00e --- /dev/null +++ b/PortabilityLayer/ResourceCompiledTypeList.h @@ -0,0 +1,16 @@ +#pragma once + +#include "ResTypeID.h" +#include "ResourceCompiledRef.h" + +#include + +namespace PortabilityLayer +{ + struct ResourceCompiledTypeList + { + ResTypeID m_resType; + ResourceCompiledRef *m_firstRef; + size_t m_numRefs; + }; +} \ No newline at end of file diff --git a/PortabilityLayer/ResourceFile.cpp b/PortabilityLayer/ResourceFile.cpp index 0df4ed7..9e7bfda 100644 --- a/PortabilityLayer/ResourceFile.cpp +++ b/PortabilityLayer/ResourceFile.cpp @@ -5,6 +5,7 @@ #include "MemoryManager.h" #include "MemReaderStream.h" #include "ResourceCompiledRef.h" +#include "ResourceCompiledTypeList.h" #include @@ -143,7 +144,8 @@ namespace PortabilityLayer return false; ByteSwap::BigUInt16(numTypesMinusOne); - m_numResourceTypes = numTypesMinusOne + 1; + // numTypesMinusOne can sometimes be -1, in which case there are no resources + m_numResourceTypes = (numTypesMinusOne + 1) & 0xffff; if (sizeFromStartOfTypeList < 2 || (sizeFromStartOfTypeList - 2) / 8 < m_numResourceTypes) return false; @@ -166,13 +168,16 @@ namespace PortabilityLayer numResourcesTotal += numResourcesOfThisType; } - m_compiledRefBlob = new ResourceCompiledRef[numResourcesTotal]; + if (numResourcesTotal > 0) + m_compiledRefBlob = new ResourceCompiledRef[numResourcesTotal]; + m_numResources = numResourcesTotal; // Second pass: Compile references ResourceCompiledRef *refToCompile = m_compiledRefBlob; - m_compiledTypeListBlob = new CompiledTypeList[m_numResourceTypes]; + if (m_numResourceTypes > 0) + m_compiledTypeListBlob = new ResourceCompiledTypeList[m_numResourceTypes]; for (uint32_t i = 0; i < m_numResourceTypes; i++) { @@ -186,7 +191,7 @@ namespace PortabilityLayer ByteSwap::BigUInt16(tlEntry.m_numResourcesMinusOne); ByteSwap::BigUInt16(tlEntry.m_refListOffset); - CompiledTypeList &ctl = m_compiledTypeListBlob[i]; + ResourceCompiledTypeList &ctl = m_compiledTypeListBlob[i]; ctl.m_resType = tlEntry.m_resType; ctl.m_firstRef = refToCompile; ctl.m_numRefs = tlEntry.m_numResourcesMinusOne + 1; @@ -318,7 +323,7 @@ namespace PortabilityLayer return a.m_resID < b.m_resID; } - bool ResourceFile::CompiledTypeListSortPredicate(const CompiledTypeList &a, const CompiledTypeList &b) + bool ResourceFile::CompiledTypeListSortPredicate(const ResourceCompiledTypeList &a, const ResourceCompiledTypeList &b) { return memcmp(&a.m_resType, &b.m_resType, sizeof(ResTypeID)) < 0; } @@ -328,18 +333,27 @@ namespace PortabilityLayer return resID - ref.m_resID; } - int ResourceFile::CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const CompiledTypeList &typeList) + int ResourceFile::CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const ResourceCompiledTypeList &typeList) { return memcmp(&resTypeID, &typeList.m_resType, 4); } + const ResourceCompiledTypeList *ResourceFile::GetResourceTypeList(const ResTypeID &resType) + { + const ResourceCompiledTypeList *tlStart = m_compiledTypeListBlob; + const ResourceCompiledTypeList *tlEnd = tlStart + m_numResourceTypes; + + const ResourceCompiledTypeList *tl = BinarySearch(tlStart, tlEnd, resType, CompiledTypeListSearchPredicate); + if (tl == tlEnd) + return nullptr; + + return tl; + } + MMHandleBlock *ResourceFile::GetResource(const ResTypeID &resType, int id, bool load) { - const CompiledTypeList *tlStart = m_compiledTypeListBlob; - const CompiledTypeList *tlEnd = tlStart + m_numResourceTypes; - - const CompiledTypeList *tl = BinarySearch(tlStart, tlEnd, resType, CompiledTypeListSearchPredicate); - if (tl == tlEnd) + const ResourceCompiledTypeList *tl = GetResourceTypeList(resType); + if (tl == nullptr) return nullptr; ResourceCompiledRef *refStart = tl->m_firstRef; @@ -366,6 +380,7 @@ namespace PortabilityLayer { void *contents = MemoryManager::GetInstance()->Alloc(resSize); handle->m_contents = contents; + handle->m_size = resSize; memcpy(handle->m_contents, ref->m_resData, resSize); } } diff --git a/PortabilityLayer/ResourceFile.h b/PortabilityLayer/ResourceFile.h index 198f870..ddfec25 100644 --- a/PortabilityLayer/ResourceFile.h +++ b/PortabilityLayer/ResourceFile.h @@ -11,25 +11,22 @@ namespace PortabilityLayer class MacFileMem; struct MMHandleBlock; struct ResourceCompiledRef; + struct ResourceCompiledTypeList; class ResTypeID; class ResourceFile { public: + ResourceFile(); ~ResourceFile(); bool Load(IOStream *stream); + const ResourceCompiledTypeList *GetResourceTypeList(const ResTypeID &resType); MMHandleBlock *GetResource(const ResTypeID &resType, int id, bool load); private: - struct CompiledTypeList - { - ResTypeID m_resType; - ResourceCompiledRef *m_firstRef; - size_t m_numRefs; - }; uint8_t *m_resDataBlob; size_t m_resDataBlobSize; @@ -40,14 +37,14 @@ namespace PortabilityLayer ResourceCompiledRef *m_compiledRefBlob; size_t m_numResources; - CompiledTypeList *m_compiledTypeListBlob; + ResourceCompiledTypeList *m_compiledTypeListBlob; size_t m_numResourceTypes; static bool CompiledRefSortPredicate(const ResourceCompiledRef &a, const ResourceCompiledRef &b); - static bool CompiledTypeListSortPredicate(const CompiledTypeList &a, const CompiledTypeList &b); + static bool CompiledTypeListSortPredicate(const ResourceCompiledTypeList &a, const ResourceCompiledTypeList &b); static int CompiledRefSearchPredicate(int resID, const ResourceCompiledRef &ref); - static int CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const CompiledTypeList &typeList); + static int CompiledTypeListSearchPredicate(const ResTypeID &resTypeID, const ResourceCompiledTypeList &typeList); }; } diff --git a/PortabilityLayer/SharedTypes.h b/PortabilityLayer/SharedTypes.h new file mode 100644 index 0000000..bb243bb --- /dev/null +++ b/PortabilityLayer/SharedTypes.h @@ -0,0 +1,139 @@ +#pragma once + +#include "PLBigEndian.h" +#include "PixelFormat.h" + +struct Point +{ + int16_t v; + int16_t h; +}; + +struct Rect +{ + int16_t top; + int16_t left; + int16_t bottom; + int16_t right; + + bool IsValid() const; + Rect Intersect(const Rect &rect) const; + Rect MakeValid() const; + static Rect Create(int16_t top, int16_t left, int16_t bottom, int16_t right); +}; + +struct Region +{ + uint16_t size; + Rect rect; +}; + + +struct BERect +{ + BEInt16_t top; + BEInt16_t left; + BEInt16_t bottom; + BEInt16_t right; + + Rect ToRect() const; +}; + +struct BERegion +{ + BEUInt16_t recordSize; + BERect rect; +}; + +struct Picture +{ + uint8_t sizeLowBytes[2]; // Low-order bytes of size, deprecated + + BERect picFrame; +}; + +struct BEBitMap +{ + BERect m_bounds; +}; + +struct BEPixMap +{ + BERect m_bounds; + BEUInt16_t m_version; + BEUInt16_t m_packType; + BEUInt32_t m_packSize; + BEUInt32_t m_hRes; + BEUInt32_t m_vRes; + BEUInt16_t m_pixelType; + BEUInt16_t m_pixelSize; + BEUInt16_t m_componentCount; + BEUInt16_t m_componentSize; + BEUInt32_t m_planeSizeBytes; + BEUInt32_t m_clutHandle; + BEUInt32_t m_unused; +}; + +struct BEColorTableHeader +{ + BEUInt32_t m_resourceID; + BEUInt16_t m_flags; // 0x8000 = indexed, 0x0000 = pixel map + BEUInt16_t m_numItemsMinusOne; +}; + +struct BEColorTableItem +{ + BEUInt16_t m_index; + uint8_t m_red[2]; + uint8_t m_green[2]; + uint8_t m_blue[2]; +}; + +struct GDevice +{ + PortabilityLayer::PixelFormat pixelFormat; +}; + +inline bool Rect::IsValid() const +{ + return this->top <= this->bottom && this->left <= this->right; +} + +inline Rect Rect::Intersect(const Rect &other) const +{ + Rect result = *this; + + if (result.left < other.left) + result.left = other.left; + if (result.right > other.right) + result.right = other.right; + if (result.top < other.top) + result.top = other.top; + if (result.bottom > other.bottom) + result.bottom = other.bottom; + + return result; +} + +inline Rect Rect::MakeValid() const +{ + Rect result = *this; + + if (result.right < result.left) + result.right = result.left; + if (result.bottom < result.top) + result.bottom = result.top; + + return result; +} + +inline Rect Rect::Create(int16_t top, int16_t left, int16_t bottom, int16_t right) +{ + Rect result; + result.top = top; + result.left = left; + result.bottom = bottom; + result.right = right; + + return result; +} diff --git a/PortabilityLayer/Vec2i.h b/PortabilityLayer/Vec2i.h new file mode 100644 index 0000000..e0966fe --- /dev/null +++ b/PortabilityLayer/Vec2i.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +namespace PortabilityLayer +{ + struct Vec2i + { + int32_t m_x; + int32_t m_y; + + Vec2i(); + Vec2i(const Vec2i &other); + Vec2i(int32_t x, int32_t y); + + Vec2i operator+(const Vec2i &other) const; + Vec2i operator-(const Vec2i &other) const; + + Vec2i &operator+=(const Vec2i &other); + Vec2i &operator-=(const Vec2i &other); + }; + + inline Vec2i::Vec2i() + : m_x(0) + , m_y(0) + { + } + + inline Vec2i::Vec2i(const Vec2i &other) + : m_x(other.m_x) + , m_y(other.m_y) + { + } + + inline Vec2i::Vec2i(int32_t x, int32_t y) + : m_x(x) + , m_y(y) + { + } + + inline Vec2i Vec2i::operator+(const Vec2i &other) const + { + return Vec2i(m_x + other.m_x, m_y + other.m_y); + } + + inline Vec2i Vec2i::operator-(const Vec2i &other) const + { + return Vec2i(m_x - other.m_x, m_y - other.m_y); + } + + inline Vec2i &Vec2i::operator+=(const Vec2i &other) + { + m_x += other.m_x; + m_y += other.m_y; + return *this; + } + + inline Vec2i &Vec2i::operator-=(const Vec2i &other) + { + m_x -= other.m_x; + m_y -= other.m_y; + return *this; + } +} + diff --git a/PortabilityLayer/WindowDef.cpp b/PortabilityLayer/WindowDef.cpp new file mode 100644 index 0000000..c881a0c --- /dev/null +++ b/PortabilityLayer/WindowDef.cpp @@ -0,0 +1,43 @@ +#include "WindowDef.h" +#include "IOStream.h" +#include "CoreDefs.h" + +namespace PortabilityLayer +{ + bool WindowDef::Deserialize(IOStream *stream) + { + struct WindowDefPart1 + { + BERect m_initialRect; + BEInt16_t m_wdefResID; + BEUInt16_t m_visibilityStatus; + BEUInt16_t m_hasCloseBox; + BEUInt32_t m_referenceConstant; + uint8_t m_titleLength; + }; + + PL_STATIC_ASSERT(sizeof(WindowDefPart1) == 19); + + WindowDefPart1 wdefPart1; + + if (stream->Read(&wdefPart1, sizeof(wdefPart1)) != sizeof(wdefPart1)) + return false; + + m_initialRect = wdefPart1.m_initialRect.ToRect(); + m_wdefResID = wdefPart1.m_wdefResID; + m_visibilityStatus = wdefPart1.m_visibilityStatus; + m_hasCloseBox = wdefPart1.m_hasCloseBox; + m_referenceConstant = wdefPart1.m_referenceConstant; + + m_title[0] = wdefPart1.m_titleLength; + if (stream->Read(m_title + 1, wdefPart1.m_titleLength) != wdefPart1.m_titleLength) + return false; + + if (stream->Read(&m_positionSpec, 2) == 2) + ByteSwap::BigUInt16(m_positionSpec); + else + m_positionSpec = 0; + + return true; + } +} diff --git a/PortabilityLayer/WindowDef.h b/PortabilityLayer/WindowDef.h new file mode 100644 index 0000000..d421973 --- /dev/null +++ b/PortabilityLayer/WindowDef.h @@ -0,0 +1,22 @@ +#pragma once + +#include "SharedTypes.h" +#include "PascalStr.h" + +namespace PortabilityLayer +{ + class IOStream; + + struct WindowDef + { + Rect m_initialRect; + int16_t m_wdefResID; + uint16_t m_visibilityStatus; + uint16_t m_hasCloseBox; + uint32_t m_referenceConstant; + uint16_t m_positionSpec; + uint8_t m_title[256]; + + bool Deserialize(IOStream *stream); + }; +} diff --git a/PortabilityLayer/WindowManager.cpp b/PortabilityLayer/WindowManager.cpp new file mode 100644 index 0000000..d96bae6 --- /dev/null +++ b/PortabilityLayer/WindowManager.cpp @@ -0,0 +1,275 @@ +#include "WindowManager.h" + +#include "DisplayDeviceManager.h" +#include "HostDisplayDriver.h" +#include "PLCore.h" +#include "MemoryManager.h" +#include "QDGraf.h" +#include "QDManager.h" +#include "WindowDef.h" + +struct GDevice; + +namespace PortabilityLayer +{ + class CGrafImpl; + + class WindowImpl final : public Window + { + public: + WindowImpl(); + ~WindowImpl(); + + bool Init(const WindowDef &windowDef, GDevice **device); + void Resize(int width, int height); + + WindowImpl *GetWindowAbove() const; + WindowImpl *GetWindowBelow() const; + + void SetWindowAbove(WindowImpl *above); + void SetWindowBelow(WindowImpl *below); + + bool IsVisible() const; + void SetVisible(bool visible); + + GDevice **GetDevice() const; + + private: + WindowImpl *m_windowAbove; + WindowImpl *m_windowBelow; + GDevice **m_device; + + bool m_visible; + }; + + class WindowManagerImpl final : public WindowManager + { + public: + WindowManagerImpl(); + + Window *CreateWindow(const WindowDef &windowDef) override; + void ResizeWindow(Window *window, int width, int height) override; + void MoveWindow(Window *window, int x, int y) override; + void PutWindowBehind(Window *window, Window *otherWindow) override; + void ShowWindow(Window *window) override; + void HideWindow(Window *window) override; + GDevice **GetWindowDevice(Window *window) override; + + Window *GetPutInFrontSentinel() const override; + + static WindowManagerImpl *GetInstance(); + + private: + void DetachWindow(Window *window); + + WindowImpl *m_windowStackTop; + WindowImpl *m_windowStackBottom; + + static WindowManagerImpl ms_instance; + static Window ms_putInFront; + }; + + WindowImpl::WindowImpl() + : m_windowAbove(nullptr) + , m_windowBelow(nullptr) + , m_device(nullptr) + , m_visible(true) + { + } + + WindowImpl::~WindowImpl() + { + PL_NotYetImplemented(); + } + + bool WindowImpl::Init(const WindowDef &windowDef, GDevice **device) + { + const Rect bounds = windowDef.m_initialRect; + + if (!bounds.IsValid()) + return false; + + const Rect adjustedBounds = Rect::Create(0, 0, bounds.bottom - bounds.top, bounds.right - bounds.left); + + if (int errorCode = m_port.Init(adjustedBounds, (*device)->pixelFormat)) + return false; + + m_device = device; + + return true; + } + + void WindowImpl::Resize(int width, int height) + { + } + + WindowImpl *WindowImpl::GetWindowAbove() const + { + return m_windowAbove; + } + + WindowImpl *WindowImpl::GetWindowBelow() const + { + return m_windowBelow; + } + + void WindowImpl::SetWindowAbove(WindowImpl *above) + { + m_windowAbove = above; + } + + void WindowImpl::SetWindowBelow(WindowImpl *below) + { + m_windowBelow = below; + } + + bool WindowImpl::IsVisible() const + { + return m_visible; + } + + void WindowImpl::SetVisible(bool visible) + { + m_visible = visible; + } + + GDevice **WindowImpl::GetDevice() const + { + return m_device; + } + + WindowManagerImpl::WindowManagerImpl() + : m_windowStackTop(nullptr) + , m_windowStackBottom(nullptr) + { + } + + Window *WindowManagerImpl::CreateWindow(const WindowDef &windowDef) + { + void *windowMem = MemoryManager::GetInstance()->Alloc(sizeof(WindowImpl)); + if (!windowMem) + return nullptr; + + GDevice **device = DisplayDeviceManager::GetInstance()->GetMainDevice(); + + Rect portRect = windowDef.m_initialRect; + if (!portRect.IsValid()) + return nullptr; + + WindowImpl *window = new (windowMem) WindowImpl(); + if (!window->Init(windowDef, device)) + { + window->~WindowImpl(); + MemoryManager::GetInstance()->Release(windowMem); + return nullptr; + } + + return window; + } + + void WindowManagerImpl::PutWindowBehind(Window *windowBase, Window *behind) + { + WindowImpl *window = static_cast(windowBase); + + if (window->GetWindowAbove() != behind) + DetachWindow(window); + + if (behind == GetPutInFrontSentinel()) + { + if (m_windowStackTop) + { + m_windowStackTop->SetWindowAbove(window); + m_windowStackTop = window; + } + else + { + m_windowStackTop = m_windowStackBottom = window; + } + } + else + { + WindowImpl *windowAbove = static_cast(behind); + WindowImpl *windowBelow = windowAbove->GetWindowBelow(); + + window->SetWindowAbove(windowAbove); + window->SetWindowBelow(windowBelow); + + if (windowBelow) + windowBelow->SetWindowAbove(window); + else + m_windowStackBottom = window; + + windowAbove->SetWindowBelow(window); + } + } + + void WindowManagerImpl::ShowWindow(Window *window) + { + static_cast(window)->SetVisible(true); + } + + void WindowManagerImpl::HideWindow(Window *window) + { + WindowImpl *impl = static_cast(window); + impl->SetVisible(false); + + // Per spec, hiding a window brings the window below it to the front + if (m_windowStackTop == impl) + { + if (WindowImpl *below = impl->GetWindowBelow()) + BringToFront(below); + } + } + + GDevice **WindowManagerImpl::GetWindowDevice(Window *window) + { + return static_cast(window)->GetDevice(); + } + + void WindowManagerImpl::ResizeWindow(Window *window, int width, int height) + { + static_cast(window)->Resize(width, height); + } + + void WindowManagerImpl::MoveWindow(Window *window, int x, int y) + { + PL_NotYetImplemented_Minor(); + } + + void WindowManagerImpl::DetachWindow(Window *window) + { + if (m_windowStackBottom == window) + m_windowStackBottom = m_windowStackBottom->GetWindowAbove(); + if (m_windowStackTop == window) + m_windowStackTop = m_windowStackTop->GetWindowBelow(); + + WindowImpl *impl = static_cast(window); + + if (WindowImpl *below = impl->GetWindowBelow()) + below->SetWindowAbove(impl->GetWindowAbove()); + + if (WindowImpl *above = impl->GetWindowAbove()) + above->SetWindowBelow(impl->GetWindowBelow()); + + impl->SetWindowAbove(nullptr); + impl->SetWindowBelow(nullptr); + } + + Window *WindowManagerImpl::GetPutInFrontSentinel() const + { + return &ms_putInFront; + } + + WindowManagerImpl *WindowManagerImpl::GetInstance() + { + return &ms_instance; + } + + WindowManagerImpl WindowManagerImpl::ms_instance; + Window WindowManagerImpl::ms_putInFront; + + WindowManager *WindowManager::GetInstance() + { + return WindowManagerImpl::GetInstance(); + } +} diff --git a/PortabilityLayer/WindowManager.h b/PortabilityLayer/WindowManager.h new file mode 100644 index 0000000..effde8d --- /dev/null +++ b/PortabilityLayer/WindowManager.h @@ -0,0 +1,25 @@ +#pragma once + +struct Window; +struct CGraf; +struct GDevice; + +namespace PortabilityLayer +{ + struct WindowDef; + + class WindowManager + { + public: + virtual Window *GetPutInFrontSentinel() const = 0; + virtual Window *CreateWindow(const WindowDef &windowDef) = 0; + virtual void ResizeWindow(Window *window, int width, int height) = 0; + virtual void MoveWindow(Window *window, int x, int y) = 0; + virtual void PutWindowBehind(Window *window, Window *otherWindow) = 0; + virtual void ShowWindow(Window *window) = 0; + virtual void HideWindow(Window *window) = 0; + virtual GDevice **GetWindowDevice(Window *window) = 0; + + static WindowManager *GetInstance(); + }; +}