
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.
637 lines
12 KiB
C++
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];
|
|
}
|