shithub: cstory

ref: 0df697d5ee9a8f5f5858cf8a3bc475c31d81206a
dir: /src/Draw.cpp/

View raw version
#include "Draw.h"

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

#include <ddraw.h>

#include "WindowsWrapper.h"

#include "CommonDefines.h"
#include "Ending.h"
#include "Generic.h"
#include "Main.h"
#include "MapName.h"
#include "TextScr.h"

typedef enum SurfaceType
{
	SURFACE_SOURCE_NONE = 1,
	SURFACE_SOURCE_RESOURCE,
	SURFACE_SOURCE_FILE
} SurfaceType;

RECT grcGame = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};
RECT grcFull = {0, 0, WINDOW_WIDTH, WINDOW_HEIGHT};

static int mag;
static BOOL fullscreen;	// TODO - Not the original variable name

static LPDIRECTDRAW lpDD;	// TODO - Not the original variable name
static LPDIRECTDRAWSURFACE frontbuffer;	// TODO - Not the original variable name
static LPDIRECTDRAWSURFACE backbuffer;	// TODO - Not the original variable name

static LPDIRECTDRAWCLIPPER clipper;	// TODO - Not the original variable name

static LPDIRECTDRAWSURFACE surf[SURFACE_ID_MAX];

static RECT backbuffer_rect;	// TODO - Not the original variable name

static int scaled_window_width;	// TODO - Not the original variable name
static int scaled_window_height;	// TODO - Not the original variable name

static HFONT font;	// TODO - Not the original variable name

// This doesn't exist in the Linux port, so none of these symbol names are accurate
static struct
{
	char name[20];
	unsigned int width;
	unsigned int height;
	SurfaceType type;
	BOOL bSystem;	// Basically a 'do not regenerate' flag
} surface_metadata[SURFACE_ID_MAX];

static int client_x;
static int client_y;

void SetClientOffset(int width, int height)
{
	client_x = width;
	client_y = height;
}

BOOL Flip_SystemTask(HWND hWnd)
{
	// TODO - Not the original variable names
	static DWORD timePrev;
	static DWORD timeNow;

	while (TRUE)
	{
		if (!SystemTask())
			return FALSE;

		// Framerate limiter
		timeNow = GetTickCount();

		if (timeNow >= timePrev + 20)
			break;

		Sleep(1);
	}

	if (timeNow >= timePrev + 100)
		timePrev = timeNow;	// If the timer is freakishly out of sync, panic and reset it, instead of spamming frames for who-knows how long
	else
		timePrev += 20;

	static RECT dst_rect;	// TODO - Not the original variable name
	GetWindowRect(hWnd, &dst_rect);
	dst_rect.left += client_x;
	dst_rect.top += client_y;
	dst_rect.right = dst_rect.left + scaled_window_width;
	dst_rect.bottom = dst_rect.top + scaled_window_height;

	frontbuffer->Blt(&dst_rect, backbuffer, &backbuffer_rect, DDBLT_WAIT, NULL);

	if (RestoreSurfaces())
	{
		RestoreStripper();
		RestoreMapName();
		RestoreTextScript();
	}

	return TRUE;
}

BOOL StartDirectDraw(HWND hWnd, int lMagnification, int lColourDepth)
{
	DDSURFACEDESC ddsd;

	if (DirectDrawCreate(NULL, &lpDD, NULL) != DD_OK)
		return FALSE;

	memset(surface_metadata, 0, sizeof(surface_metadata));

	switch (lMagnification)
	{
		case 0:
			mag = 1;
			fullscreen = FALSE;
			lpDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
			break;

		case 1:
			mag = 2;
			fullscreen = FALSE;
			lpDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);
			break;

		case 2:
			mag = 2;
			fullscreen = TRUE;
			lpDD->SetCooperativeLevel(hWnd, DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE);
			lpDD->SetDisplayMode(WINDOW_WIDTH * mag, WINDOW_HEIGHT * mag, lColourDepth);
			break;
	}

	backbuffer_rect.left = 0;
	backbuffer_rect.top = 0;
	backbuffer_rect.right = WINDOW_WIDTH * mag;
	backbuffer_rect.bottom = WINDOW_HEIGHT * mag;

	scaled_window_width = WINDOW_WIDTH * mag;
	scaled_window_height = WINDOW_HEIGHT * mag;

	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS;
	ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
	ddsd.dwBackBufferCount = 0;

	if (lpDD->CreateSurface(&ddsd, &frontbuffer, NULL) != DD_OK)
		return FALSE;

	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwWidth = WINDOW_WIDTH * mag;
	ddsd.dwHeight = WINDOW_HEIGHT * mag;

	if (lpDD->CreateSurface(&ddsd, &backbuffer, NULL) != DD_OK)
		return FALSE;

	lpDD->CreateClipper(0, &clipper, NULL);
	clipper->SetHWnd(0, hWnd);
	frontbuffer->SetClipper(clipper);

	return TRUE;
}

void EndDirectDraw(HWND hWnd)
{
	int i;

	// Release all surfaces
	for (i = 0; i < SURFACE_ID_MAX; ++i)
	{
		if (surf[i] != NULL)
		{
			surf[i]->Release();
			surf[i] = NULL;
		}
	}

	if (frontbuffer != NULL)
	{
		frontbuffer->Release();
		frontbuffer = NULL;
		backbuffer = NULL;
	}

	if (fullscreen)
		lpDD->SetCooperativeLevel(hWnd, DDSCL_NORMAL);

	if (lpDD != NULL)
	{
		lpDD->Release();
		lpDD = NULL;
	}

	memset(surface_metadata, 0, sizeof(surface_metadata));
}

void ReleaseSurface(SurfaceID s)
{
	// Release the surface we want to release
	if (surf[s] != NULL)
	{
		surf[s]->Release();
		surf[s] = NULL;
	}

	memset(&surface_metadata[s], 0, sizeof(surface_metadata[0]));
}

// TODO - Inaccurate stack frame
BOOL MakeSurface_Resource(const char *name, SurfaceID surf_no)
{
	if (surf_no >= SURFACE_ID_MAX)
		return FALSE;

	if (surf[surf_no] != NULL)
		return FALSE;

	HANDLE handle = LoadImageA(GetModuleHandleA(NULL), name, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
	if (handle == NULL)
		return FALSE;

	BITMAP bitmap;
	GetObjectA(handle, sizeof(BITMAP), &bitmap);

	DDSURFACEDESC ddsd;
	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwWidth = bitmap.bmWidth * mag;
	ddsd.dwHeight = bitmap.bmHeight * mag;

	if (lpDD->CreateSurface(&ddsd, &surf[surf_no], NULL) != DD_OK)
		return FALSE;

	int src_x = 0;
	int src_y = 0;
	int src_w = bitmap.bmWidth;
	int src_h = bitmap.bmHeight;

	int dst_x = 0;
	int dst_y = 0;
	int dst_w = bitmap.bmWidth * mag;
	int dst_h = bitmap.bmHeight * mag;

	HDC hdc = CreateCompatibleDC(NULL);
	HGDIOBJ hgdiobj = SelectObject(hdc, handle);

	HDC hdc2;
	surf[surf_no]->GetDC(&hdc2);
	StretchBlt(hdc2, dst_x, dst_y, dst_w, dst_h, hdc, src_x, src_y, src_w, src_h, SRCCOPY);
	surf[surf_no]->ReleaseDC(hdc2);

	SelectObject(hdc, hgdiobj);
	DeleteDC(hdc);

	DDCOLORKEY ddcolorkey;
	ddcolorkey.dwColorSpaceLowValue = 0;
	ddcolorkey.dwColorSpaceHighValue = 0;

	surf[surf_no]->SetColorKey(DDCKEY_SRCBLT, &ddcolorkey);
	surf[surf_no]->SetClipper(clipper);

#ifdef FIX_MAJOR_BUGS
	DeleteObject(handle);
#endif

	surface_metadata[surf_no].type = SURFACE_SOURCE_RESOURCE;
	surface_metadata[surf_no].width = bitmap.bmWidth;
	surface_metadata[surf_no].height = bitmap.bmHeight;
	surface_metadata[surf_no].bSystem = FALSE;
	strcpy(surface_metadata[surf_no].name, name);

	return TRUE;
}

// TODO - Inaccurate stack frame
BOOL MakeSurface_File(const char *name, SurfaceID surf_no)
{
	char path[MAX_PATH];
	sprintf(path, "%s\\%s.pbm", gDataPath, name);

	if (!IsEnableBitmap(path))
	{
		ErrorLog(path, 0);
		return FALSE;
	}

#ifdef FIX_BUGS
	if (surf_no >= SURFACE_ID_MAX)
#else
	if (surf_no > SURFACE_ID_MAX)
#endif
	{
		ErrorLog("surface no", surf_no);
		return FALSE;
	}

	if (surf[surf_no] != NULL)
	{
		ErrorLog("existing", surf_no);
		return FALSE;
	}

	HANDLE handle = LoadImageA(GetModuleHandleA(NULL), path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
	if (handle == NULL)
	{
		ErrorLog(path, 1);
		return FALSE;
	}

	BITMAP bitmap;
	GetObjectA(handle, sizeof(BITMAP), &bitmap);

	DDSURFACEDESC ddsd;
	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;
	ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;
	ddsd.dwWidth = bitmap.bmWidth * mag;
	ddsd.dwHeight = bitmap.bmHeight * mag;

	lpDD->CreateSurface(&ddsd, &surf[surf_no], NULL);

	int src_x = 0;
	int src_y = 0;
	int src_w = bitmap.bmWidth;
	int src_h = bitmap.bmHeight;

	int dst_x = 0;
	int dst_y = 0;
	int dst_w = bitmap.bmWidth * mag;
	int dst_h = bitmap.bmHeight * mag;

	HDC hdc = CreateCompatibleDC(NULL);
	HGDIOBJ hgdiobj = SelectObject(hdc, handle);

	HDC hdc2;
	surf[surf_no]->GetDC(&hdc2);
	StretchBlt(hdc2, dst_x, dst_y, dst_w, dst_h, hdc, src_x, src_y, src_w, src_h, SRCCOPY);
	surf[surf_no]->ReleaseDC(hdc2);

	SelectObject(hdc, hgdiobj);
	DeleteDC(hdc);

	DDCOLORKEY ddcolorkey;
	ddcolorkey.dwColorSpaceLowValue = 0;
	ddcolorkey.dwColorSpaceHighValue = 0;

	surf[surf_no]->SetColorKey(DDCKEY_SRCBLT, &ddcolorkey);
	surf[surf_no]->SetClipper(clipper);

	DeleteObject(handle);

	surface_metadata[surf_no].type = SURFACE_SOURCE_FILE;
	surface_metadata[surf_no].width = bitmap.bmWidth;
	surface_metadata[surf_no].height = bitmap.bmHeight;
	surface_metadata[surf_no].bSystem = FALSE;
	strcpy(surface_metadata[surf_no].name, name);

	return TRUE;
}

// TODO - Inaccurate stack frame
BOOL ReloadBitmap_Resource(const char *name, SurfaceID surf_no)
{
	if (surf_no >= SURFACE_ID_MAX)
		return FALSE;

	HANDLE handle = LoadImageA(GetModuleHandleA(NULL), name, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
	if (handle == NULL)
		return FALSE;

	BITMAP bitmap;
	GetObjectA(handle, sizeof(BITMAP), &bitmap);

	int src_x = 0;
	int src_y = 0;
	int src_w = bitmap.bmWidth;
	int src_h = bitmap.bmHeight;

	int dst_x = 0;
	int dst_y = 0;
	int dst_w = bitmap.bmWidth * mag;
	int dst_h = bitmap.bmHeight * mag;

	HDC hdc = CreateCompatibleDC(NULL);
	HGDIOBJ hgdiobj = SelectObject(hdc, handle);

	HDC hdc2;
	surf[surf_no]->GetDC(&hdc2);
	StretchBlt(hdc2, dst_x, dst_y, dst_w, dst_h, hdc, src_x, src_y, src_w, src_h, SRCCOPY);
	surf[surf_no]->ReleaseDC(hdc2);

	SelectObject(hdc, hgdiobj);
	DeleteDC(hdc);

	DDCOLORKEY ddcolorkey;
	ddcolorkey.dwColorSpaceLowValue = 0;
	ddcolorkey.dwColorSpaceHighValue = 0;

	surf[surf_no]->SetColorKey(DDCKEY_SRCBLT, &ddcolorkey);
	surf[surf_no]->SetClipper(clipper);

#ifdef FIX_MAJOR_BUGS
	DeleteObject(handle);
#endif

	surface_metadata[surf_no].type = SURFACE_SOURCE_RESOURCE;
	strcpy(surface_metadata[surf_no].name, name);

	return TRUE;
}

// TODO - Inaccurate stack frame
BOOL ReloadBitmap_File(const char *name, SurfaceID surf_no)
{
	char path[MAX_PATH];
	sprintf(path, "%s\\%s.pbm", gDataPath, name);

	if (!IsEnableBitmap(path))
	{
		ErrorLog(path, 0);
		return FALSE;
	}

#ifdef FIX_BUGS
	if (surf_no >= SURFACE_ID_MAX)
#else
	if (surf_no > SURFACE_ID_MAX)
#endif
	{
		ErrorLog("surface no", surf_no);
		return FALSE;
	}

	HANDLE handle = LoadImageA(GetModuleHandleA(NULL), path, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
	if (handle == NULL)
	{
		ErrorLog(path, 1);
		return FALSE;
	}

	BITMAP bitmap;
	GetObjectA(handle, sizeof(BITMAP), &bitmap);

	int src_x = 0;
	int src_y = 0;
	int src_w = bitmap.bmWidth;
	int src_h = bitmap.bmHeight;

	int dst_x = 0;
	int dst_y = 0;
	int dst_w = bitmap.bmWidth * mag;
	int dst_h = bitmap.bmHeight * mag;

	HDC hdc = CreateCompatibleDC(NULL);
	HGDIOBJ hgdiobj = SelectObject(hdc, handle);

	HDC hdc2;
	surf[surf_no]->GetDC(&hdc2);
	StretchBlt(hdc2, dst_x, dst_y, dst_w, dst_h, hdc, src_x, src_y, src_w, src_h, SRCCOPY);
	surf[surf_no]->ReleaseDC(hdc2);

	SelectObject(hdc, hgdiobj);
	DeleteDC(hdc);

	// No colour-keying

	DeleteObject(handle);

	surface_metadata[surf_no].type = SURFACE_SOURCE_FILE;
	strcpy(surface_metadata[surf_no].name, name);

	return TRUE;
}

// TODO - Inaccurate stack frame
BOOL MakeSurface_Generic(int bxsize, int bysize, SurfaceID surf_no, BOOL bSystem)
{
#ifdef FIX_BUGS
	if (surf_no >= SURFACE_ID_MAX)
#else
	if (surf_no > SURFACE_ID_MAX)	// OOPS (should be '>=')
#endif
		return FALSE;

	if (surf[surf_no] != NULL)
		return FALSE;

	DDSURFACEDESC ddsd;
	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);
	ddsd.dwFlags = DDSD_CAPS | DDSD_HEIGHT | DDSD_WIDTH;

	if (bSystem)
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_SYSTEMMEMORY;
	else
		ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN;

	ddsd.dwWidth = bxsize * mag;
	ddsd.dwHeight = bysize * mag;

	lpDD->CreateSurface(&ddsd, &surf[surf_no], NULL);

	DDCOLORKEY ddcolorkey;
	ddcolorkey.dwColorSpaceLowValue = 0;
	ddcolorkey.dwColorSpaceHighValue = 0;

	surf[surf_no]->SetColorKey(DDCKEY_SRCBLT, &ddcolorkey);

	surface_metadata[surf_no].type = SURFACE_SOURCE_NONE;
	surface_metadata[surf_no].width = ddsd.dwWidth / mag;
	surface_metadata[surf_no].height = ddsd.dwHeight / mag;

	if (bSystem)
		surface_metadata[surf_no].bSystem = TRUE;
	else
		surface_metadata[surf_no].bSystem = FALSE;

	strcpy(surface_metadata[surf_no].name, "generic");

	return TRUE;
}

void BackupSurface(SurfaceID surf_no, const RECT *rect)
{
	static DDBLTFX ddbltfx;	// TODO - Not the original variable name

	memset(&ddbltfx, 0, sizeof(DDBLTFX));
	ddbltfx.dwSize = sizeof(DDBLTFX);

	static RECT rcSet;	// TODO - Not the original variable name
	rcSet.left = rect->left * mag;
	rcSet.top = rect->top * mag;
	rcSet.right = rect->right * mag;
	rcSet.bottom = rect->bottom * mag;

	surf[surf_no]->Blt(&rcSet, backbuffer, &rcSet, DDBLT_WAIT, &ddbltfx);
}

void PutBitmap3(const RECT *rcView, int x, int y, const RECT *rect, SurfaceID surf_no) // Transparency
{
	static RECT rcWork;
	static RECT rcSet;

	rcWork = *rect;

	if (x + rect->right - rect->left > rcView->right)
		rcWork.right -= (x + rect->right - rect->left) - rcView->right;

	if (x < rcView->left)
	{
		rcWork.left += rcView->left - x;
		x = rcView->left;
	}

	if (y + rect->bottom - rect->top > rcView->bottom)
		rcWork.bottom -= (y + rect->bottom - rect->top) - rcView->bottom;

	if (y < rcView->top)
	{
		rcWork.top += rcView->top - y;
		y = rcView->top;
	}

	rcSet.left = x;
	rcSet.top = y;
	rcSet.right = x + rcWork.right - rcWork.left;
	rcSet.bottom = y + rcWork.bottom - rcWork.top;

	rcWork.left *= mag;
	rcWork.top *= mag;
	rcWork.right *= mag;
	rcWork.bottom *= mag;

	rcSet.left *= mag;
	rcSet.top *= mag;
	rcSet.right *= mag;
	rcSet.bottom *= mag;

	backbuffer->Blt(&rcSet, surf[surf_no], &rcWork, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
}

void PutBitmap4(const RECT *rcView, int x, int y, const RECT *rect, SurfaceID surf_no) // No Transparency
{
	static RECT rcWork;
	static RECT rcSet;

	rcWork = *rect;

	if (x + rect->right - rect->left > rcView->right)
		rcWork.right -= (x + rect->right - rect->left) - rcView->right;

	if (x < rcView->left)
	{
		rcWork.left += rcView->left - x;
		x = rcView->left;
	}

	if (y + rect->bottom - rect->top > rcView->bottom)
		rcWork.bottom -= (y + rect->bottom - rect->top) - rcView->bottom;

	if (y < rcView->top)
	{
		rcWork.top += rcView->top - y;
		y = rcView->top;
	}

	rcSet.left = x;
	rcSet.top = y;
	rcSet.right = x + rcWork.right - rcWork.left;
	rcSet.bottom = y + rcWork.bottom - rcWork.top;

	rcWork.left *= mag;
	rcWork.top *= mag;
	rcWork.right *= mag;
	rcWork.bottom *= mag;

	rcSet.left *= mag;
	rcSet.top *= mag;
	rcSet.right *= mag;
	rcSet.bottom *= mag;

	backbuffer->Blt(&rcSet, surf[surf_no], &rcWork, DDBLT_WAIT, NULL);
}

void Surface2Surface(int x, int y, const RECT *rect, SurfaceID to, SurfaceID from)
{
	static RECT rcWork;
	static RECT rcSet;

	rcWork.left = rect->left * mag;
	rcWork.top = rect->top * mag;
	rcWork.right = rect->right * mag;
	rcWork.bottom = rect->bottom * mag;

	rcSet.left = x;
	rcSet.top = y;
	rcSet.right = x + rect->right - rect->left;
	rcSet.bottom = y + rect->bottom - rect->top;

	rcSet.left *= mag;
	rcSet.top *= mag;
	rcSet.right *= mag;
	rcSet.bottom *= mag;

	surf[to]->Blt(&rcSet, surf[from], &rcWork, DDBLT_KEYSRC | DDBLT_WAIT, NULL);
}

// This converts a colour to the 'native' format by writing it
// straight to the framebuffer, and then reading it back
unsigned long GetCortBoxColor(COLORREF col)
{
	HDC hdc;

	if (backbuffer->GetDC(&hdc) != DD_OK)
		return 0xFFFFFFFF;

	COLORREF original_colour = GetPixel(hdc, 0, 0);
	SetPixel(hdc, 0, 0, col);
	backbuffer->ReleaseDC(hdc);

	DDSURFACEDESC ddsd;
	memset(&ddsd, 0, sizeof(DDSURFACEDESC));
	ddsd.dwSize = sizeof(DDSURFACEDESC);

	if (backbuffer->Lock(NULL, &ddsd, DDLOCK_WAIT, NULL) != DD_OK)
		return 0xFFFFFFFF;

	DWORD native_colour = *(DWORD*)ddsd.lpSurface;

	if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
		native_colour &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1;

	backbuffer->Unlock(0);

	if (backbuffer->GetDC(&hdc) != DD_OK)
		return 0xFFFFFFFF;

	SetPixel(hdc, 0, 0, original_colour);
	backbuffer->ReleaseDC(hdc);

	return native_colour;
}

void CortBox(const RECT *rect, unsigned long col)
{
	static DDBLTFX ddbltfx;	// TODO - Not the original variable name
	memset(&ddbltfx, 0, sizeof(DDBLTFX));
	ddbltfx.dwSize = sizeof(DDBLTFX);
	ddbltfx.dwFillColor = col;

	static RECT rcSet;	// TODO - Not the original variable name
	rcSet.left = rect->left * mag;
	rcSet.top = rect->top * mag;
	rcSet.right = rect->right * mag;
	rcSet.bottom = rect->bottom * mag;

	backbuffer->Blt(&rcSet, 0, 0, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
}

void CortBox2(const RECT *rect, unsigned long col, SurfaceID surf_no)
{
	static DDBLTFX ddbltfx;	// TODO - Not the original variable name
	memset(&ddbltfx, 0, sizeof(DDBLTFX));
	ddbltfx.dwSize = sizeof(DDBLTFX);
	ddbltfx.dwFillColor = col;

	static RECT rcSet;	// TODO - Not the original variable name
	rcSet.left = rect->left * mag;
	rcSet.top = rect->top * mag;
	rcSet.right = rect->right * mag;
	rcSet.bottom = rect->bottom * mag;

	surface_metadata[surf_no].type = SURFACE_SOURCE_NONE;

	surf[surf_no]->Blt(&rcSet, 0, 0, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
}

// Dummied-out log function
// According to the Mac port, its name really is just "out".
BOOL out(char surface_identifier)
{
	// The actual name (and type) of these two variables are unknown
	char path[MAX_PATH];
	FILE *fp;

	(void)surface_identifier;
	(void)path;
	(void)fp;

	// There may have been some kind of 'OutputDebugStringA' call here,
	// like the one in 'EnumDevices_Callback' in 'Input.cpp'.
	// Pixel may have kept them wrapped in '#ifdef DEBUG' blocks.

	return TRUE;
}

// TODO - Probably not the original function name (this is an educated guess)
int RestoreSurfaces(void)
{
	int s;
	RECT rect;
	int surfaces_regenerated = 0;

	if (frontbuffer == NULL)
		return surfaces_regenerated;

	if (backbuffer == NULL)
		return surfaces_regenerated;

	if (frontbuffer->IsLost() == DDERR_SURFACELOST)
	{
		++surfaces_regenerated;
		frontbuffer->Restore();
		out('f');	// 'f' for 'frontbuffer'
	}

	if (backbuffer->IsLost() == DDERR_SURFACELOST)
	{
		++surfaces_regenerated;
		backbuffer->Restore();
		out('b');	// 'b' for 'backbuffer'
	}

	for (s = 0; s < SURFACE_ID_MAX; ++s)
	{
		if (surf[s] != NULL)
		{
			if (surf[s]->IsLost() == DDERR_SURFACELOST)
			{
				++surfaces_regenerated;
				surf[s]->Restore();
				out('0' + s);	// The number of the surface lost

				if (!surface_metadata[s].bSystem)
				{
					switch (surface_metadata[s].type)
					{
						case SURFACE_SOURCE_NONE:
							rect.left = 0;
							rect.top = 0;
							rect.right = surface_metadata[s].width;
							rect.bottom = surface_metadata[s].height;
							CortBox2(&rect, 0, (SurfaceID)s);
							break;

						case SURFACE_SOURCE_RESOURCE:
							ReloadBitmap_Resource(surface_metadata[s].name, (SurfaceID)s);
							break;

						case SURFACE_SOURCE_FILE:
							ReloadBitmap_File(surface_metadata[s].name, (SurfaceID)s);
							break;
					}
				}
			}
		}
	}

	return surfaces_regenerated;
}

// TODO - Inaccurate stack frame
void InitTextObject(const char *name)
{
	// Get font size
	unsigned int width, height;

	// Let me tell you why these font sizes are unfortunate...
	// 6x12 is a good font size - fonts use high-quality bitmaps at that
	// size, and it works with Cave Story's internal assumption that
	// characters are spaced 6 pixels apart.
	// The sad part is the 10x20 size: you might be wondering why Pixel
	// didn't use 12x24 instead. Well, that's because fonts don't use
	// bitmaps at that size - instead you get ugly low-res vector
	// renders. So, Pixel had to use 10x20 instead. But there's a
	// problem: this means the characters are spaced 5 pixels apart
	// instead. This normally isn't a problem because the game usually
	// hardcodes it, but this isn't the case when either <SAT is used, a
	// texture is regenerated, or when the game prints the name of the
	// map. This causes the font to render with incorrect spacing.
	// There's really not much of a solution to this, especially when
	// you consider that the English translation was specifically built
	// around the broken spacing.

	switch (mag)
	{
		case 1:
			height = 12;
			width = 6;
			break;

		case 2:
			height = 20;
			width = 10;
			break;
	}

	// The game uses DEFAULT_CHARSET when it should have used SHIFTJIS_CHARSET.
	// This breaks the Japanese text on English Windows installations.

	// Also, the game uses DEFAULT_QUALITY instead of NONANTIALIASED_QUALITY,
	// causing font antialiasing to blend with the colour-key, giving text an
	// ugly black outline.
#ifdef FIX_BUGS
	#define QUALITY NONANTIALIASED_QUALITY
	#ifdef JAPANESE
		#define CHARSET SHIFTJIS_CHARSET
	#else
		#define CHARSET DEFAULT_CHARSET
	#endif
#else
	#define QUALITY DEFAULT_QUALITY
	#define CHARSET DEFAULT_CHARSET
#endif

	font = CreateFontA(height, width, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, QUALITY, FIXED_PITCH | FF_DONTCARE, name);

	if (font == NULL)
		font = CreateFontA(height, width, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, QUALITY, FIXED_PITCH | FF_DONTCARE, NULL);
}

void PutText(int x, int y, const char *text, unsigned long color)
{
	HDC hdc;
	backbuffer->GetDC(&hdc);
	HGDIOBJ hgdiobj = SelectObject(hdc, font);
	SetBkMode(hdc, 1);
	SetTextColor(hdc, color);
	TextOutA(hdc, x * mag, y * mag, text, (int)strlen(text));
	SelectObject(hdc, hgdiobj);
	backbuffer->ReleaseDC(hdc);
}

void PutText2(int x, int y, const char *text, unsigned long color, SurfaceID surf_no)
{
	HDC hdc;
	surf[surf_no]->GetDC(&hdc);
	HGDIOBJ hgdiobj = SelectObject(hdc, font);
	SetBkMode(hdc, 1);
	SetTextColor(hdc, color);
	TextOutA(hdc, x * mag, y * mag, text, (int)strlen(text));
	SelectObject(hdc, hgdiobj);
	surf[surf_no]->ReleaseDC(hdc);
}

void EndTextObject(void)
{
	DeleteObject(font);
}