cave-story-solaris/src/Main.cpp
Clownacy fe76fe6dea Big scary messy unfinished font overhaul
This is currently hardcoded to 640x480 English builds.

What I've done is temporarily gutted the FreeType2 font renderer, and
instead introduced a system where the font is read from a
pre-rendered font atlas. This allows me to get 100% accurate font
rendering to Windows XP... because I literally created these atlases
with Windows XP.

So not only does this eliminate the issue of FreeType producing
misshapen glyphs, but it also works around the copyright issue
regarding bundling the Courier New and MS Gothic fonts with CSE2.

You see, font files can be copyrighted like any old software, however
typefaces have a lot less protection - they're basically fair-game.
IANAL, of course - look it up yourself.

I don't really want to throw away the FreeType font system since I
spent a ton of time working on it, it allows you to use other font
files, and I'll probably wind up needing it for CSE2EX. I guess I'll
just have to find some way to either have the two systems coexist, or
make it so one or the other can be selected at compile-time.
2020-09-17 15:02:36 +01:00

637 lines
12 KiB
C++

#include "Main.h"
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "WindowsWrapper.h"
#include "Backends/Misc.h"
#include "Bitmap.h"
#include "CommonDefines.h"
#include "Config.h"
#include "Draw.h"
#include "Game.h"
#include "Generic.h"
#include "Input.h"
#include "KeyControl.h"
#include "MyChar.h"
#include "Organya.h"
#include "Profile.h"
#include "Resource.h"
#include "Sound.h"
#include "Triangle.h"
void InactiveWindow(void);
void ActiveWindow(void);
std::string gModulePath;
std::string gDataPath;
BOOL bFullscreen;
BOOL gbUseJoystick = FALSE;
int gJoystickButtonTable[8];
static BOOL bActive = TRUE;
static BOOL bFPS = FALSE;
static int windowWidth;
static int windowHeight;
#ifdef JAPANESE
static const char* const lpWindowName = "洞窟物語"; // "Cave Story"
#else
static const char* const lpWindowName = "Cave Story ~ Doukutsu Monogatari";
#endif
static void DragAndDropCallback(const char *path)
{
LoadProfile(path);
}
static void WindowFocusCallback(bool focus)
{
if (focus)
ActiveWindow();
else
InactiveWindow();
}
// Framerate stuff
static unsigned long CountFramePerSecound(void)
{
unsigned long current_tick; // The original name for this variable is unknown
static BOOL first = TRUE;
static unsigned long max_count;
static unsigned long count;
static unsigned long wait;
if (first)
{
wait = Backend_GetTicks();
first = FALSE;
}
current_tick = Backend_GetTicks();
++count;
if (wait + 1000 <= current_tick)
{
wait += 1000;
max_count = count;
count = 0;
}
return max_count;
}
void PutFramePerSecound(void)
{
if (bFPS)
{
const unsigned long fps = CountFramePerSecound();
PutNumber4(WINDOW_WIDTH - 40, 8, fps, FALSE);
}
}
// TODO - Inaccurate stack frame
int main(int argc, char *argv[])
{
(void)argc;
int i;
if (!Backend_Init(DragAndDropCallback, WindowFocusCallback))
return EXIT_FAILURE;
// Get executable's path
if (!Backend_GetBasePath(&gModulePath))
{
// Fall back on argv[0] if the backend cannot provide a path
gModulePath = argv[0];
for (size_t i = gModulePath.length();; --i)
{
if (i == 0 || gModulePath[i] == '\\' || gModulePath[i] == '/')
{
gModulePath.resize(i);
break;
}
}
}
// Get path of the data folder
gDataPath = gModulePath + "/data";
CONFIGDATA conf;
if (!LoadConfigData(&conf))
DefaultConfigData(&conf);
// Apply keybinds
// Swap X and Z buttons
switch (conf.attack_button_mode)
{
case 0:
gKeyJump = KEY_Z;
gKeyShot = KEY_X;
break;
case 1:
gKeyJump = KEY_X;
gKeyShot = KEY_Z;
break;
}
// Swap Okay and Cancel buttons
switch (conf.ok_button_mode)
{
case 0:
gKeyOk = gKeyJump;
gKeyCancel = gKeyShot;
break;
case 1:
gKeyOk = gKeyShot;
gKeyCancel = gKeyJump;
break;
}
// Swap left and right weapon switch keys
if (IsKeyFile("s_reverse"))
{
gKeyArms = KEY_ARMSREV;
gKeyArmsRev = KEY_ARMS;
}
// Alternate movement keys
switch (conf.move_button_mode)
{
case 0:
gKeyLeft = KEY_LEFT;
gKeyUp = KEY_UP;
gKeyRight = KEY_RIGHT;
gKeyDown = KEY_DOWN;
break;
case 1:
gKeyLeft = KEY_ALT_LEFT;
gKeyUp = KEY_ALT_UP;
gKeyRight = KEY_ALT_RIGHT;
gKeyDown = KEY_ALT_DOWN;
break;
}
// Set gamepad inputs
for (i = 0; i < 8; ++i)
{
switch (conf.joystick_button[i])
{
case 1:
gJoystickButtonTable[i] = gKeyJump;
break;
case 2:
gJoystickButtonTable[i] = gKeyShot;
break;
case 3:
gJoystickButtonTable[i] = gKeyArms;
break;
case 6:
gJoystickButtonTable[i] = gKeyArmsRev;
break;
case 4:
gJoystickButtonTable[i] = gKeyItem;
break;
case 5:
gJoystickButtonTable[i] = gKeyMap;
break;
}
}
RECT unused_rect = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
switch (conf.display_mode)
{
case 1:
case 2:
// Set window dimensions
if (conf.display_mode == 1)
{
windowWidth = WINDOW_WIDTH;
windowHeight = WINDOW_HEIGHT;
}
else
{
windowWidth = WINDOW_WIDTH * 2;
windowHeight = WINDOW_HEIGHT * 2;
}
#ifdef FIX_MAJOR_BUGS
if (conf.display_mode == 1)
{
if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 0))
{
Backend_Deinit();
return EXIT_FAILURE;
}
}
else
{
if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 1))
{
Backend_Deinit();
return EXIT_FAILURE;
}
}
#else
// Doesn't handle StartDirectDraw failing
if (conf.display_mode == 1)
StartDirectDraw(lpWindowName, windowWidth, windowHeight, 0);
else
StartDirectDraw(lpWindowName, windowWidth, windowHeight, 1);
#endif
break;
case 0:
case 3:
case 4:
// Set window dimensions
windowWidth = WINDOW_WIDTH * 2;
windowHeight = WINDOW_HEIGHT * 2;
#ifdef FIX_MAJOR_BUGS
if (!StartDirectDraw(lpWindowName, windowWidth, windowHeight, 2))
{
Backend_Deinit();
return EXIT_FAILURE;
}
#else
// Doesn't handle StartDirectDraw failing
StartDirectDraw(lpWindowName, windowWidth, windowHeight, 2);
#endif
bFullscreen = TRUE;
Backend_HideMouse();
break;
}
#ifdef DEBUG_SAVE
Backend_EnableDragAndDrop();
#endif
// Set up window icon
#ifndef _WIN32 // On Windows, we use native icons instead (so we can give the taskbar and window separate icons, like the original EXE does)
size_t window_icon_resource_size;
const unsigned char *window_icon_resource_data = FindResource("ICON_MINI", "ICON", &window_icon_resource_size);
if (window_icon_resource_data != NULL)
{
size_t window_icon_width, window_icon_height;
unsigned char *window_icon_rgb_pixels = DecodeBitmap(window_icon_resource_data, window_icon_resource_size, &window_icon_width, &window_icon_height, 3);
if (window_icon_rgb_pixels != NULL)
{
Backend_SetWindowIcon(window_icon_rgb_pixels, window_icon_width, window_icon_height);
FreeBitmap(window_icon_rgb_pixels);
}
}
#endif
// Set up the cursor
size_t cursor_resource_size;
const unsigned char *cursor_resource_data = FindResource("CURSOR_NORMAL", "CURSOR", &cursor_resource_size);
if (cursor_resource_data != NULL)
{
size_t cursor_width, cursor_height;
unsigned char *cursor_rgb_pixels = DecodeBitmap(cursor_resource_data, cursor_resource_size, &cursor_width, &cursor_height, 3);
if (cursor_rgb_pixels != NULL)
{
Backend_SetCursor(cursor_rgb_pixels, cursor_width, cursor_height);
FreeBitmap(cursor_rgb_pixels);
}
}
if (IsKeyFile("fps"))
bFPS = TRUE;
// Set rects
RECT rcLoading = {0, 0, 64, 8};
RECT rcFull = {0, 0, 0, 0};
rcFull.right = WINDOW_WIDTH;
rcFull.bottom = WINDOW_HEIGHT;
// Load the "LOADING" text
BOOL b = MakeSurface_File("Loading", SURFACE_ID_LOADING);
// Draw loading screen
CortBox(&rcFull, 0x000000);
PutBitmap3(&rcFull, (WINDOW_WIDTH / 2) - 32, (WINDOW_HEIGHT / 2) - 4, &rcLoading, SURFACE_ID_LOADING);
// Draw to screen
if (!Flip_SystemTask())
{
Backend_Deinit();
return EXIT_SUCCESS;
}
// Initialize sound
InitDirectSound();
// Initialize joystick
if (conf.bJoystick && InitDirectInput())
{
ResetJoystickStatus();
gbUseJoystick = TRUE;
}
// Initialize stuff
InitTextObject(conf.font_name);
InitTriangleTable();
// Run game code
Game();
// End stuff
EndTextObject();
EndDirectSound();
EndDirectDraw();
Backend_Deinit();
return EXIT_SUCCESS;
}
void InactiveWindow(void)
{
if (bActive)
{
bActive = FALSE;
StopOrganyaMusic();
SleepNoise();
}
PlaySoundObject(7, SOUND_MODE_STOP);
}
void ActiveWindow(void)
{
if (!bActive)
{
bActive = TRUE;
StopOrganyaMusic();
PlayOrganyaMusic();
ResetNoise();
}
PlaySoundObject(7, SOUND_MODE_PLAY_LOOP);
}
void JoystickProc(void);
BOOL SystemTask(void)
{
static bool previous_keyboard_state[BACKEND_KEYBOARD_TOTAL];
do
{
if (!Backend_SystemTask(bActive))
{
StopOrganyaMusic();
return FALSE;
}
} while(!bActive);
bool keyboard_state[BACKEND_KEYBOARD_TOTAL];
Backend_GetKeyboardState(keyboard_state);
for (unsigned int i = 0; i < BACKEND_KEYBOARD_TOTAL; ++i)
{
if (keyboard_state[i] && !previous_keyboard_state[i])
{
switch (i)
{
case BACKEND_KEYBOARD_ESCAPE:
gKey |= KEY_ESCAPE;
break;
case BACKEND_KEYBOARD_W:
gKey |= KEY_MAP;
break;
case BACKEND_KEYBOARD_LEFT:
gKey |= KEY_LEFT;
break;
case BACKEND_KEYBOARD_RIGHT:
gKey |= KEY_RIGHT;
break;
case BACKEND_KEYBOARD_UP:
gKey |= KEY_UP;
break;
case BACKEND_KEYBOARD_DOWN:
gKey |= KEY_DOWN;
break;
case BACKEND_KEYBOARD_X:
gKey |= KEY_X;
break;
case BACKEND_KEYBOARD_Z:
gKey |= KEY_Z;
break;
case BACKEND_KEYBOARD_S:
gKey |= KEY_ARMS;
break;
case BACKEND_KEYBOARD_A:
gKey |= KEY_ARMSREV;
break;
case BACKEND_KEYBOARD_LEFT_SHIFT:
case BACKEND_KEYBOARD_RIGHT_SHIFT:
gKey |= KEY_SHIFT;
break;
case BACKEND_KEYBOARD_F1:
gKey |= KEY_F1;
break;
case BACKEND_KEYBOARD_F2:
gKey |= KEY_F2;
break;
case BACKEND_KEYBOARD_Q:
gKey |= KEY_ITEM;
break;
case BACKEND_KEYBOARD_COMMA:
gKey |= KEY_ALT_LEFT;
break;
case BACKEND_KEYBOARD_PERIOD:
gKey |= KEY_ALT_DOWN;
break;
case BACKEND_KEYBOARD_FORWARD_SLASH:
gKey |= KEY_ALT_RIGHT;
break;
case BACKEND_KEYBOARD_L:
gKey |= KEY_L;
break;
case BACKEND_KEYBOARD_EQUALS:
gKey |= KEY_PLUS;
break;
case BACKEND_KEYBOARD_F5:
gbUseJoystick = FALSE;
break;
}
}
else if (!keyboard_state[i] && previous_keyboard_state[i])
{
switch (i)
{
case BACKEND_KEYBOARD_ESCAPE:
gKey &= ~KEY_ESCAPE;
break;
case BACKEND_KEYBOARD_W:
gKey &= ~KEY_MAP;
break;
case BACKEND_KEYBOARD_LEFT:
gKey &= ~KEY_LEFT;
break;
case BACKEND_KEYBOARD_RIGHT:
gKey &= ~KEY_RIGHT;
break;
case BACKEND_KEYBOARD_UP:
gKey &= ~KEY_UP;
break;
case BACKEND_KEYBOARD_DOWN:
gKey &= ~KEY_DOWN;
break;
case BACKEND_KEYBOARD_X:
gKey &= ~KEY_X;
break;
case BACKEND_KEYBOARD_Z:
gKey &= ~KEY_Z;
break;
case BACKEND_KEYBOARD_S:
gKey &= ~KEY_ARMS;
break;
case BACKEND_KEYBOARD_A:
gKey &= ~KEY_ARMSREV;
break;
case BACKEND_KEYBOARD_LEFT_SHIFT:
case BACKEND_KEYBOARD_RIGHT_SHIFT:
gKey &= ~KEY_SHIFT;
break;
case BACKEND_KEYBOARD_F1:
gKey &= ~KEY_F1;
break;
case BACKEND_KEYBOARD_F2:
gKey &= ~KEY_F2;
break;
case BACKEND_KEYBOARD_Q:
gKey &= ~KEY_ITEM;
break;
case BACKEND_KEYBOARD_COMMA:
gKey &= ~KEY_ALT_LEFT;
break;
case BACKEND_KEYBOARD_PERIOD:
gKey &= ~KEY_ALT_DOWN;
break;
case BACKEND_KEYBOARD_FORWARD_SLASH:
gKey &= ~KEY_ALT_RIGHT;
break;
case BACKEND_KEYBOARD_L:
gKey &= ~KEY_L;
break;
case BACKEND_KEYBOARD_EQUALS:
gKey &= ~KEY_PLUS;
break;
}
}
}
memcpy(previous_keyboard_state, keyboard_state, sizeof(keyboard_state));
// Run joystick code
if (gbUseJoystick)
JoystickProc();
return TRUE;
}
void JoystickProc(void)
{
int i;
DIRECTINPUTSTATUS status;
if (!GetJoystickStatus(&status))
return;
gKey &= (KEY_ESCAPE | KEY_F1 | KEY_F2);
// Set movement buttons
if (status.bLeft)
gKey |= gKeyLeft;
else
gKey &= ~gKeyLeft;
if (status.bRight)
gKey |= gKeyRight;
else
gKey &= ~gKeyRight;
if (status.bUp)
gKey |= gKeyUp;
else
gKey &= ~gKeyUp;
if (status.bDown)
gKey |= gKeyDown;
else
gKey &= ~gKeyDown;
// Clear held buttons
for (i = 0; i < 8; ++i)
gKey &= ~gJoystickButtonTable[i];
// Set held buttons
for (i = 0; i < 8; ++i)
if (status.bButton[i])
gKey |= gJoystickButtonTable[i];
}