shithub: cstory

ref: 7f75ad6ef8ca9c1a2b87f860e4287b32c14845f3
dir: /src/Main.cpp/

View raw version
#include "Main.h"

#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#include <SDL.h>

#include "WindowsWrapper.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"
#include "Types.h"

// These two are defined in Draw.cpp. This is a bit of a hack.
extern SDL_Window *gWindow;
extern SDL_Renderer *gRenderer;

char gModulePath[PATH_LENGTH];
char gDataPath[PATH_LENGTH];

int gJoystickButtonTable[8];

bool gbUseJoystick = false;
bool bFps = false;

bool bActive = true;

#ifdef JAPANESE
const char *lpWindowName = "洞窟物語エンジン2";
#else
const char *lpWindowName = "Cave Story Engine 2 ~ Doukutsu Monogatari Enjin 2";
#endif

// A replication of MSVC's rand algorithm
static unsigned long int next = 1;

int rep_rand()
{
	next = ((next) * 214013 + 2531011);
	return ((next) >> 16) & 0x7FFF;
}

void rep_srand(unsigned int seed)
{
	next = seed;
}

// Framerate stuff
void PutFramePerSecound()
{
	if (bFps)
		PutNumber4(WINDOW_WIDTH - 40, 8, GetFramePerSecound(), false);
}

int GetFramePerSecound()
{
	unsigned int current_tick;
	static bool need_new_base_tick = true;
	static int frames_this_second;
	static int current_frame;
	static int base_tick;

	if (need_new_base_tick)
	{
		base_tick = SDL_GetTicks();
		need_new_base_tick = false;
	}

	current_tick = SDL_GetTicks();
	++current_frame;

	if (base_tick + 1000 <= current_tick)
	{
		base_tick += 1000;
		frames_this_second = current_frame;
		current_frame = 0;
	}

	return frames_this_second;
}

int main(int argc, char *argv[])
{
	// Get executable's path
	strcpy(gModulePath, SDL_GetBasePath());
	if (gModulePath[strlen(gModulePath) - 1] == '/' || gModulePath[strlen(gModulePath) - 1] == '\\')
		gModulePath[strlen(gModulePath) - 1] = '\0'; // String cannot end in slash or stuff will probably break (original does this through a windows.h provided function)

	// Get path of the data folder
	strcpy(gDataPath, gModulePath);
	strcat(gDataPath, "/data");

#ifdef WINDOWS
	// Set the window icons. See res/ICON/ICON.rc.
	SDL_SetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON, "101");
	SDL_SetHint(SDL_HINT_WINDOWS_INTRESOURCE_ICON_SMALL, "102");
#endif

	// Initialize SDL
	if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS | SDL_INIT_TIMER) >= 0)
	{
		// Load configuration
		CONFIG config;

		if (!LoadConfigData(&config))
			DefaultConfigData(&config);

		// Apply keybinds
		// Swap X and Z buttons
		if (config.attack_button_mode)
		{
			if (config.attack_button_mode == 1)
			{
				gKeyJump = KEY_X;
				gKeyShot = KEY_Z;
			}
		}
		else
		{
			gKeyJump = KEY_Z;
			gKeyShot = KEY_X;
		}

		// Swap Okay and Cancel buttons
		if (config.ok_button_mode)
		{
			if (config.ok_button_mode == 1)
			{
				gKeyOk = gKeyShot;
				gKeyCancel = gKeyJump;
			}
		}
		else
		{
			gKeyOk = gKeyJump;
			gKeyCancel = gKeyShot;
		}

		// Swap left and right weapon switch keys
		if (CheckFileExists("s_reverse"))
		{
			gKeyArms = KEY_ARMSREV;
			gKeyArmsRev = KEY_ARMS;
		}

		// Alternate movement keys
		if (config.move_button_mode)
		{
			if (config.move_button_mode == 1)
			{
				gKeyLeft = KEY_ALT_LEFT;
				gKeyUp = KEY_ALT_UP;
				gKeyRight = KEY_ALT_RIGHT;
				gKeyDown = KEY_ALT_DOWN;
			}
		}
		else
		{
			gKeyLeft = KEY_LEFT;
			gKeyUp = KEY_UP;
			gKeyRight = KEY_RIGHT;
			gKeyDown = KEY_DOWN;
		}

		// Set gamepad inputs
		for (int i = 0; i < 8; i++)
		{
			switch (config.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;

				default:
					continue;
			}
		}

		RECT unused_rect = {0, 0, 320, 240};

		// Load cursor
		size_t size;
		const unsigned char *data = FindResource("CURSOR_NORMAL", "CURSOR", &size);

		if (data)
		{
			SDL_RWops *fp = SDL_RWFromConstMem(data, size);

			SDL_Surface *cursor_surface = SDL_LoadBMP_RW(fp, 1);
			SDL_SetColorKey(cursor_surface, SDL_TRUE, SDL_MapRGB(cursor_surface->format, 0xFF, 0, 0xFF));	// Pink regions are transparent

			SDL_Cursor *cursor = SDL_CreateColorCursor(cursor_surface, 0, 0);	// Don't worry, the hotspots are accurate to the original files

			if (cursor)
				SDL_SetCursor(cursor);
			else
				printf("Failed to load cursor\n");

			SDL_FreeSurface(cursor_surface);
		}
		else
		{
			printf("Failed to load cursor\n");
		}

		// Get window dimensions and colour depth
		int windowWidth;
		int windowHeight;
		int colourDepth;

		switch (config.display_mode)
		{
			case 1:
			case 2:
				// Set window dimensions
				if (config.display_mode == 1)
				{
					windowWidth = WINDOW_WIDTH;
					windowHeight = WINDOW_HEIGHT;
				}
				else
				{
					windowWidth = WINDOW_WIDTH * 2;
					windowHeight = WINDOW_HEIGHT * 2;
				}

				// Create window
				gWindow = SDL_CreateWindow(lpWindowName, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, 0);

				if (gWindow)
				{
					if (config.display_mode == 1)
						StartDirectDraw(0, 0);
					else
						StartDirectDraw(1, 0);
					break;
				}

				break;

			case 0:
			case 3:
			case 4:
				// Set window dimensions
				windowWidth = WINDOW_WIDTH * 2;
				windowHeight = WINDOW_HEIGHT * 2;

				// Create window
				gWindow = SDL_CreateWindow(lpWindowName, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, windowWidth, windowHeight, 0);

				if (gWindow)
				{
					// Set colour depth
					switch (config.display_mode)
					{
						case 0:
							colourDepth = 16;
							break;
						case 3:
							colourDepth = 24;
							break;
						case 4:
							colourDepth = 32;
							break;
					}

					StartDirectDraw(2, colourDepth);

					fullscreen = true;
					SDL_ShowCursor(0);
					break;
				}
				break;
		}

		// Create window


		if (gWindow)
		{
			// Check debug things
			if (CheckFileExists("fps"))
				bFps = true;

#ifndef WINDOWS
			// Load icon
			size_t size;
			const unsigned char *data = FindResource("ICON_MINI", "ICON", &size);

			if (data)
			{
				SDL_RWops *fp = SDL_RWFromConstMem(data, size);
				SDL_Surface *iconSurf = SDL_LoadBMP_RW(fp, 1);
				SDL_Surface *iconConverted = SDL_ConvertSurfaceFormat(iconSurf, SDL_PIXELFORMAT_RGB888, 0);
				SDL_FreeSurface(iconSurf);
				SDL_Surface *iconSurfUpscaled = SDL_CreateRGBSurfaceWithFormat(0, 256, 256, 0, SDL_PIXELFORMAT_RGB888);
				SDL_LowerBlitScaled(iconConverted, NULL, iconSurfUpscaled, NULL);
				SDL_FreeSurface(iconConverted);
				SDL_SetWindowIcon(gWindow, iconSurfUpscaled);
				SDL_FreeSurface(iconSurfUpscaled);
			}
			else
			{
				printf("Failed to load icon\n");
			}
#endif

			// Set rects
			RECT loading_rect = {0, 0, 64, 8};
			RECT clip_rect = {0, 0, windowWidth, windowHeight};

			// Load the "LOADING" text
			MakeSurface_File("Loading", SURFACE_ID_LOADING);

			// Draw loading screen
			CortBox(&clip_rect, 0x000000);
			PutBitmap3(&clip_rect, (WINDOW_WIDTH - 64) / 2, (WINDOW_HEIGHT - 8) / 2, &loading_rect, SURFACE_ID_LOADING);

			// Draw to screen
			if (Flip_SystemTask())
			{
				// Initialize sound
				InitDirectSound();

				// Initialize joystick
				if (config.bJoystick && InitDirectInput())
				{
					ResetJoystickStatus();
					gbUseJoystick = true;
				}

				// Initialize stuff
				InitTextObject(config.font_name);
				InitTriangleTable();

				// Run game code
				Game();

				// End stuff
				EndDirectSound();
				EndTextObject();
				EndDirectDraw();
			}
		}
	}
	else
	{
		SDL_Quit();
		return -1;
	}

	SDL_Quit();
	return 0;
}

void InactiveWindow()
{
	if (bActive)
	{
		bActive = false;
		StopOrganyaMusic();
		SleepNoise();
	}

	PlaySoundObject(7, 0);
}

void ActiveWindow()
{
	if (!bActive)
	{
		bActive = true;
		StopOrganyaMusic();
		PlayOrganyaMusic();
		ResetNoise();
	}

	PlaySoundObject(7, -1);
}

void JoystickProc()
{
	JOYSTICK_STATUS status;

	if (GetJoystickStatus(&status))
	{
		// Clear held buttons
		gKey &= (KEY_ESCAPE | KEY_F2 | KEY_F1);

		// Set movement buttons
		if (status.bLeft)
			gKey |= gKeyLeft;
		if (status.bRight)
			gKey |= gKeyRight;
		if (status.bUp)
			gKey |= gKeyUp;
		if (status.bDown)
			gKey |= gKeyDown;

		// Set held buttons
		for (int i = 0; i < 8; i++)
		{
			if (status.bButton[i])
				gKey |= gJoystickButtonTable[i];
		}
	}
}

#define DO_KEY_PRESS(key) \
	if (event.type == SDL_KEYDOWN) \
		gKey |= key; \
	else \
		gKey &= ~key; \
	break;

bool SystemTask()
{
	// Handle window events
	bool focusGained = true;

	while (SDL_PollEvent(NULL) || !focusGained)
	{
		SDL_Event event;
		SDL_WaitEvent(&event);

		switch (event.type)
		{
			case SDL_QUIT:
				return false;
				break;

			case SDL_WINDOWEVENT:
				switch (event.window.event)
				{
					case SDL_WINDOWEVENT_FOCUS_GAINED:
						focusGained = true;
						ActiveWindow();
						break;

					case SDL_WINDOWEVENT_FOCUS_LOST:
						focusGained = false;
						InactiveWindow();
						break;

					default:
						break;
				}
				break;

			case SDL_DROPFILE:
				LoadProfile(event.drop.file);
				SDL_free(event.drop.file);
				break;

			case SDL_KEYDOWN:
			case SDL_KEYUP:
#ifdef FIX_BUGS
				// BUG FIX: Pixel relied on key codes for input, but these differ based on keyboard layout.
				// This would break the alternate movement keys on typical English keyboards, since the '=' key is in a completely different place to where it is on a Japanese keyboard.
				// To solve this, we use scancodes instead, which are based on the physical location of keys, rather than their meaning.
				switch (event.key.keysym.scancode)
				{
					case SDL_SCANCODE_ESCAPE:
						DO_KEY_PRESS(KEY_ESCAPE)

					case SDL_SCANCODE_W:
						DO_KEY_PRESS(KEY_MAP)

					case SDL_SCANCODE_LEFT:
						DO_KEY_PRESS(KEY_LEFT)

					case SDL_SCANCODE_RIGHT:
						DO_KEY_PRESS(KEY_RIGHT)

					case SDL_SCANCODE_UP:
						DO_KEY_PRESS(KEY_UP)

					case SDL_SCANCODE_DOWN:
						DO_KEY_PRESS(KEY_DOWN)

					case SDL_SCANCODE_X:
						DO_KEY_PRESS(KEY_X)

					case SDL_SCANCODE_Z:
						DO_KEY_PRESS(KEY_Z)

					case SDL_SCANCODE_S:
						DO_KEY_PRESS(KEY_ARMS)

					case SDL_SCANCODE_A:
						DO_KEY_PRESS(KEY_ARMSREV)

					case SDL_SCANCODE_RSHIFT:
					case SDL_SCANCODE_LSHIFT:
						DO_KEY_PRESS(KEY_SHIFT)

					case SDL_SCANCODE_F1:
						DO_KEY_PRESS(KEY_F1)

					case SDL_SCANCODE_F2:
						DO_KEY_PRESS(KEY_F2)

					case SDL_SCANCODE_Q:
						DO_KEY_PRESS(KEY_ITEM)

					case SDL_SCANCODE_COMMA:
						DO_KEY_PRESS(KEY_ALT_LEFT)

					case SDL_SCANCODE_PERIOD:
						DO_KEY_PRESS(KEY_ALT_DOWN)

					case SDL_SCANCODE_SLASH:
						DO_KEY_PRESS(KEY_ALT_RIGHT)

					case SDL_SCANCODE_L:
						DO_KEY_PRESS(KEY_ALT_UP)

					case SDL_SCANCODE_SEMICOLON:
						DO_KEY_PRESS(KEY_PLUS)

					case SDL_SCANCODE_F5:
						gbUseJoystick = false;
						break;

					default:
						break;
				}
				break;
#else
				switch (event.key.keysym.sym)
				{
					case SDLK_ESCAPE:
						DO_KEY_PRESS(KEY_ESCAPE)

					case SDLK_w:
						DO_KEY_PRESS(KEY_MAP)

					case SDLK_LEFT:
						DO_KEY_PRESS(KEY_LEFT)

					case SDLK_RIGHT:
						DO_KEY_PRESS(KEY_RIGHT)

					case SDLK_UP:
						DO_KEY_PRESS(KEY_UP)

					case SDLK_DOWN:
						DO_KEY_PRESS(KEY_DOWN)

					case SDLK_x:
						DO_KEY_PRESS(KEY_X)

					case SDLK_z:
						DO_KEY_PRESS(KEY_Z)

					case SDLK_s:
						DO_KEY_PRESS(KEY_ARMS)

					case SDLK_a:
						DO_KEY_PRESS(KEY_ARMSREV)

					case SDLK_RSHIFT:
					case SDLK_LSHIFT:
						DO_KEY_PRESS(KEY_SHIFT)

					case SDLK_F1:
						DO_KEY_PRESS(KEY_F1)

					case SDLK_F2:
						DO_KEY_PRESS(KEY_F2)

					case SDLK_q:
						DO_KEY_PRESS(KEY_ITEM)

					case SDLK_COMMA:
						DO_KEY_PRESS(KEY_ALT_LEFT)

					case SDLK_PERIOD:
						DO_KEY_PRESS(KEY_ALT_DOWN)

					case SDLK_SLASH:
						DO_KEY_PRESS(KEY_ALT_RIGHT)

					case SDLK_l:
						DO_KEY_PRESS(KEY_ALT_UP)

					case SDLK_SEMICOLON:
						DO_KEY_PRESS(KEY_PLUS)

					case SDLK_F5:
						gbUseJoystick = false;
						break;
				}
				break;
#endif
		}
	}

	// Run joystick code
	if (gbUseJoystick)
		JoystickProc();

	return true;
}