Files
mcpe/platforms/windows/AppPlatform_win32.cpp
iProgramInCpp 60b21356a1 Integrate touch related development. (#90)
* * Add BuildActionIntention crap

* * Set Client and World projects to use MP compilation

* asd

* * Use the new BuildActionIntention to break and place blocks.

* * Reverse engineer the IArea system.

* * Copy break logic from survival into creative conditionally

* * Reverse IBuildInput and MouseHandler
* Replace the new relative paths in the client project with $(MC_ROOT) again

* * Reverse Multitouch, MouseDevice

* * Reverse a bunch of auxiliary classes for input.

* * Use CustomInputHolder instead of holding inputs manually.

* * Reverse a whole BUNCH of things!

* * Add feedback textures to the gitignore.

* * D-pad now renders! Also loads of other work.

* * More Stuff

* * Finish touch control bug fixing.

* * Finalize work.

* * One last thing..

* * Add a "cramped" mode to the options screen and start menu.

* * Oh, forgot to do something
2023-11-02 00:49:11 +02:00

392 lines
8.2 KiB
C++

/********************************************************************
Minecraft: Pocket Edition - Decompilation Project
Copyright (C) 2023 iProgramInCpp
The following code is licensed under the BSD 1 clause license.
SPDX-License-Identifier: BSD-1-Clause
********************************************************************/
#define WIN32_LEAN_AND_MEAN
#include <fstream>
#include <sstream>
#include <shlobj.h>
#include "GameMods.hpp"
#include "AppPlatform_win32.hpp"
#include "LoggerWin32.hpp"
#include "thirdparty/GL/GL.hpp"
#include "thirdparty/stb_image.h"
#include "thirdparty/stb_image_write.h"
AppPlatform_win32::AppPlatform_win32()
{
m_WindowTitle = "ReMinecraftPE";
// just assume an 854x480 window for now:
m_ScreenWidth = C_DEFAULT_SCREEN_WIDTH;
m_ScreenHeight = C_DEFAULT_SCREEN_HEIGHT;
m_UserInputStatus = -1;
m_bIsFocused = false;
m_bGrabbedMouse = false;
m_bActuallyGrabbedMouse = false;
m_bWasUnfocused = false;
m_bShiftPressed = false;
m_MouseDiffX = 0, m_MouseDiffY = 0;
// This initializes the Logger singleton to use the Windows-specific variant
// If we didn't initialize it here, the Minecraft class would have our back
m_pLogger = new LoggerWin32();
m_pSoundSystem = nullptr;
}
AppPlatform_win32::~AppPlatform_win32()
{
SAFE_DELETE(m_pSoundSystem);
// DELETE THIS LAST
SAFE_DELETE(m_pLogger);
}
void AppPlatform_win32::initSoundSystem()
{
if (!m_pSoundSystem)
m_pSoundSystem = new SoundSystemDS();
else
LOG_E("Trying to initialize SoundSystem more than once!");
}
int AppPlatform_win32::checkLicense()
{
// we own the game!!
return 1;
}
void AppPlatform_win32::buyGame()
{
MessageBoxA(GetHWND(), "Buying the game!", getWindowTitle(), MB_OK | MB_ICONINFORMATION);
}
void AppPlatform_win32::saveScreenshot(const std::string& fileName, int width, int height)
{
int npixels = width * height;
uint32_t* pixels = new uint32_t[npixels];
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
stbi_flip_vertically_on_write(true);
// Verify if the folder exists for saving screenshots and
// create it if it doesn't
// Kinda inefficient but I didn't want to be too intrusive
// -Vruk
// https://stackoverflow.com/a/22182041
// https://stackoverflow.com/a/8901001
CHAR mypicturespath[MAX_PATH];
HRESULT result = SHGetFolderPathA(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, mypicturespath);
static char str[MAX_PATH];
if (result == S_OK)
sprintf(str, "%s\\%s", mypicturespath, "MCPE");
else
sprintf(str, "%s\\%s", ".", "Screenshots");
// https://stackoverflow.com/a/8233867
DWORD ftyp = GetFileAttributesA(str);
DWORD error = GetLastError();
if (error == ERROR_PATH_NOT_FOUND || error == ERROR_FILE_NOT_FOUND || error == ERROR_INVALID_NAME)
{
// https://stackoverflow.com/a/22182041
CreateDirectoryA(str, NULL);
}
char fullpath[MAX_PATH];
sprintf(fullpath, "%s\\%s", str, fileName.c_str());
stbi_write_png(fullpath, width, height, 4, pixels, width * 4);
delete[] pixels;
}
void AppPlatform_win32::createUserInput()
{
m_UserInput.clear();
m_UserInputStatus = -1;
switch (m_DialogType)
{
case DLG_CREATE_WORLD:
{
// some placeholder for now
m_UserInput.push_back("New World");
m_UserInput.push_back("123456");
m_UserInputStatus = 1;
break;
}
}
}
void AppPlatform_win32::showDialog(eDialogType type)
{
m_DialogType = type;
}
std::string AppPlatform_win32::getDateString(int time)
{
time_t tt = time;
struct tm t;
// using the _s variant. For a different platform there's gmtime_r. This is not directly portable however.
gmtime_s(&t, &tt);
//format it with strftime
char buf[2048];
strftime(buf, sizeof buf, "%b %d %Y %H:%M:%S", &t);
//strftime(buf, sizeof buf, "%a %b %d %H:%M:%S %Z %Y", &t);
return std::string(buf);
}
Texture AppPlatform_win32::loadTexture(const std::string& str, bool b)
{
std::string realPath = str;
if (realPath.size() && realPath[0] == '/')
// trim it off
realPath = realPath.substr(1);
realPath = "assets/" + realPath;
FILE* f = fopen(realPath.c_str(), "rb");
if (!f)
{
LOG_E("File %s couldn't be opened", realPath.c_str());
_error:
const std::string msg = "Error loading " + realPath + ". Did you unzip the Minecraft assets?";
MessageBoxA(GetHWND(), msg.c_str(), getWindowTitle(), MB_OK);
if (f)
fclose(f);
::exit(1);
}
int width = 0, height = 0, channels = 0;
stbi_uc* img = stbi_load_from_file(f, &width, &height, &channels, STBI_rgb_alpha);
if (!img)
{
LOG_E("File %s couldn't be loaded via stb_image", realPath.c_str());
goto _error;
}
uint32_t* img2 = new uint32_t[width * height];
memcpy(img2, img, width * height * sizeof(uint32_t));
stbi_image_free(img);
img = nullptr;
fclose(f);
return Texture(width, height, img2, 1, 0);
}
bool AppPlatform_win32::isTouchscreen()
{
return false;
}
bool AppPlatform_win32::hasFileSystemAccess()
{
return true;
}
std::string AppPlatform_win32::getPatchData()
{
std::ifstream ifs("assets/patches/patch_data.txt");
if (!ifs.is_open())
return "";
std::stringstream ss;
ss << ifs.rdbuf();
return ss.str();
}
void AppPlatform_win32::setScreenSize(int width, int height)
{
m_ScreenWidth = width;
m_ScreenHeight = height;
}
void AppPlatform_win32::recenterMouse()
{
// only recenter the mouse if it's grabbed
if (!m_bGrabbedMouse)
return;
// If we aren't the foreground window, return
if (GetForegroundWindow() != GetHWND())
{
m_bWasUnfocused = true;
return;
}
POINT oldPos = { 0, 0 };
GetCursorPos(&oldPos);
RECT rect;
GetClientRect(GetHWND(), &rect);
POINT offs = { m_ScreenWidth / 2, m_ScreenHeight / 2 };
ClientToScreen(GetHWND(), &offs);
SetCursorPos(offs.x, offs.y);
// Note. The only reason we do it this way instead of
// using the Mouse class is because, after SetCursorPos,
// we'll get an event on our window telling us "hey, the
// user has moved their cursor back to the center! Move
// the camera back as well", causing a camera that just
// refuses to move
// If we were unfocused last frame, ignore the diff data we have.
if (!m_bWasUnfocused)
{
m_MouseDiffX -= offs.x - oldPos.x;
m_MouseDiffY -= offs.y - oldPos.y;
}
m_bWasUnfocused = false;
}
void AppPlatform_win32::setMouseGrabbed(bool b)
{
m_bGrabbedMouse = b;
if (m_bActuallyGrabbedMouse == (b && m_bIsFocused))
return;
if (!b || !m_bIsFocused)
{
m_bActuallyGrabbedMouse = false;
//show the cursor
ShowCursor(TRUE);
//unconfine it
ClipCursor(NULL);
clearDiff();
}
else
{
m_bActuallyGrabbedMouse = true;
//hide the cursor
ShowCursor(FALSE);
//confine it to our client area
RECT rect;
GetClientRect(GetHWND(), &rect);
POINT offs = { 0, 0 };
ClientToScreen(GetHWND(), &offs);
rect.left += offs.x;
rect.top += offs.y;
rect.right += offs.x;
rect.bottom += offs.y;
ClipCursor(&rect);
// set the cursor pos to the middle of the screen
recenterMouse();
clearDiff();
}
}
void AppPlatform_win32::getMouseDiff(int& x, int& y)
{
x = m_MouseDiffX;
y = m_MouseDiffY;
}
void AppPlatform_win32::clearDiff()
{
m_MouseDiffX = m_MouseDiffY = 0;
}
void AppPlatform_win32::updateFocused(bool focused)
{
m_bIsFocused = focused;
setMouseGrabbed(m_bGrabbedMouse);
}
MouseButtonType AppPlatform_win32::GetMouseButtonType(UINT iMsg)
{
switch (iMsg)
{
case WM_LBUTTONUP:
case WM_LBUTTONDOWN:
return BUTTON_LEFT;
case WM_RBUTTONUP:
case WM_RBUTTONDOWN:
return BUTTON_RIGHT;
case WM_MBUTTONUP:
case WM_MBUTTONDOWN:
return BUTTON_MIDDLE;
case WM_MOUSEWHEEL:
return BUTTON_SCROLLWHEEL;
default:
return BUTTON_NONE;
}
}
bool AppPlatform_win32::GetMouseButtonState(UINT iMsg, WPARAM wParam)
{
bool result;
switch (iMsg)
{
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
result = true;
break;
case WM_MOUSEWHEEL:
{
short wheelDelta = GET_WHEEL_DELTA_WPARAM(wParam);
if (wheelDelta > 0)
{
// "A positive value indicates that the wheel was rotated forward, away from the user."
result = false;
}
else
{
// "A negative value indicates that the wheel was rotated backward, toward the user."
result = true;
}
break;
}
default:
result = false;
break;
}
return result;
}
Keyboard::KeyState AppPlatform_win32::GetKeyState(UINT iMsg)
{
switch (iMsg)
{
case WM_KEYUP:
return Keyboard::KeyState::UP;
case WM_KEYDOWN:
default:
return Keyboard::KeyState::DOWN;
}
}