Split off the Windows message handler into a separate thread.

This commit is contained in:
Fatbag 2012-04-13 22:55:13 -05:00
parent d9afae7022
commit 4294cb41f7
11 changed files with 547 additions and 217 deletions

View file

@ -7,7 +7,6 @@ include_directories(${CMAKE_SOURCE_DIR}/Libraries/freetype/include)
if(WIN32)
set(NIOTSOCLIENT_SOURCES
Client.cpp
MessageHandler.cpp
Audio/Startup.cpp
Audio/windows/XAudio2.cpp
Graphics/Font.cpp
@ -15,7 +14,8 @@ if(WIN32)
Graphics/Viewport.cpp
resources/Resource.rc
System/System.cpp
Window/Window.cpp
)
add_executable(TSO WIN32 ${NIOTSOCLIENT_SOURCES})
add_executable(TSO ${NIOTSOCLIENT_SOURCES})
target_link_libraries(TSO FileHandler_shared freetype_shared ole32 opengl32)
endif()

View file

@ -17,15 +17,7 @@
#include "EngineInterface.hpp"
namespace Window {
unsigned Width, Height;
bool Fullscreen = false;
HWND hWnd = NULL;
}
int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen);
void Shutdown();
Scene * CurrentScene;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
@ -33,18 +25,22 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
int result;
//Disallow multiple instances of the game from running
CreateMutex(NULL, TRUE, "TSO_NIOTSO_MUTEX");
if(GetLastError() == ERROR_ALREADY_EXISTS){
if(CreateMutex(NULL, TRUE, "TSO_NIOTSO_MUTEX") == NULL){
HWND hWnd = FindWindow("TSO_NIOTSO", "The Sims Online");
if(hWnd != NULL){
ShowWindowAsync(hWnd, SW_RESTORE);
ShowWindow(hWnd, SW_RESTORE);
SetForegroundWindow(hWnd);
SetFocus(hWnd);
}
return ERROR_INIT_ANOTHERINSTRUNNING;
}
Window::Width = 800;
Window::Height = 600;
Window::Fullscreen = false;
System::hInst = hInstance;
result = CreateWindowInvisible(hInstance, 800, 600, false);
result = Window::Initialize();
if(result != 0){
Shutdown();
return ERROR_INIT | ERROR_INIT_WINDOW | result;
@ -82,17 +78,15 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
QueryPerformanceCounter(&PreviousTime);
while(true){
MSG msg;
while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
LARGE_INTEGER CurrentTime;
QueryPerformanceCounter(&CurrentTime);
float TimeDelta = (float)(CurrentTime.QuadPart-PreviousTime.QuadPart)/System::ClockFreq.QuadPart;
if(TimeDelta < 0 || TimeDelta >= 5) //Invalid TimeDelta
if(TimeDelta < 0 || TimeDelta >= 5){ //Invalid TimeDelta
PreviousTime.QuadPart = CurrentTime.QuadPart;
continue;
}
memcpy(&System::UserInput, (const void*) &System::UserInput_v, sizeof(System::UserInput_t));
int result = CurrentScene->RunFor(TimeDelta);
if(result == System::SHUTDOWN)
@ -117,89 +111,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
return 0;
}
int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen)
{
const WNDCLASS wc = {
CS_OWNDC, //style
WndProc, //lpfnWndProc
0, //cbClsExtra
0, //cbWndExtra
hInst, //hInstance
(HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_TSO), //hIcon
IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE),
NULL, //hCursor
NULL, //hbrBackground
NULL, //lpszMenuName
"TSO_NIOTSO" //lpszClassName
};
if(!RegisterClass(&wc)){
MessageBox(NULL, "Failed to register the window class.", NULL, MB_OK | MB_ICONERROR);
return ERROR_REGISTER_CLASS;
}
SetCursor(NULL);
HWND hWnd = NULL;
if(Fullscreen){
DEVMODE dmScreenSettings;
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
dmScreenSettings.dmPelsWidth = Width;
dmScreenSettings.dmPelsHeight = Height;
dmScreenSettings.dmBitsPerPel = 32;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){
MessageBox(NULL, "Fullscreen mode is not supported by your setup. It has been turned off.", NULL,
MB_OK | MB_ICONERROR);
Fullscreen = 0;
}else{
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online", WS_POPUP,
0, 0, Width, Height, 0, 0, hInst, NULL);
}
}
if(hWnd == NULL){
Fullscreen = false;
RECT WindowRect = {0, 0, Width, Height};
//Use a style of WS_OVERLAPPEDWINDOW to allow resizing
AdjustWindowRectEx(&WindowRect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE,
WS_EX_APPWINDOW); //This finds the dimensions of a window with a client area of our specified dimensions
//Note: Windows can be positioned anywhere, even outside the visible workspace,
//but their sizes are limited to the size of the workspace on the primary display.
unsigned WindowWidth = WindowRect.right-WindowRect.left, WindowHeight = WindowRect.bottom-WindowRect.top;
RECT WorkspaceRect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkspaceRect, 0);
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
((WorkspaceRect.right-WorkspaceRect.left - WindowWidth)>>1) + WorkspaceRect.left,
((WorkspaceRect.bottom-WorkspaceRect.top - WindowHeight)>>1) + WorkspaceRect.top,
WindowWidth, WindowHeight, 0, 0, hInst, NULL);
}
if(hWnd == NULL){
MessageBox(NULL, "Failed to create the window.", NULL, MB_OK | MB_ICONERROR);
return ERROR_CREATE_WINDOW;
}
Window::hWnd = hWnd;
Window::Width = Width;
Window::Height = Height;
Window::Fullscreen = Fullscreen;
System::hInst = hInst;
return 0;
}
void Shutdown()
{
Audio::Shutdown();
Graphics::Shutdown();
System::Shutdown();
if(Window::hWnd){
DestroyWindow(Window::hWnd);
Window::hWnd = NULL;
}
UnregisterClass("TSO_NIOTSO", System::hInst);
Window::Shutdown();
}

View file

@ -15,18 +15,25 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//Compiler/platform constants
#define WINVER 0x0502
#define _WIN32_WINNT 0x0502
#define _CRT_SECURE_NO_WARNINGS
//Standard libraries
#include <stdint.h>
#include <stdio.h>
#include <memory.h>
#include <windows.h>
#undef NULL
#define NULL 0
//Codebase libraries
#include "FileHandler.hpp"
#include "ft2build.h"
#include FT_FREETYPE_H
//Macro definitions
#ifndef min
#define min(x,y) ((x)<(y)?(x):(y))
#endif
@ -34,20 +41,14 @@
#define max(x,y) ((x)>(y)?(x):(y))
#endif
//IsometricEngine.cpp
namespace Window {
extern unsigned Width, Height;
extern bool Fullscreen;
extern HWND hWnd;
}
//MessageHandler.cpp
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
//Codebase version, branding, linker resources
#include "version.h"
#include "System/System.hpp"
#include "Resources/Resource.h"
//Components
#include "Audio/Audio.hpp"
#include "Graphics/Graphics.hpp"
#include "System/System.hpp"
#include "Window/Window.hpp"
#include "Scene/Scene.hpp"

View file

@ -1,59 +0,0 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "EngineInterface.hpp"
/****
For the sake of performance **all throughout the game**,
order the cases in order from most frequently occurred to less frequently occurred
except making adjustments where tiny response latency is essential
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_SETCURSOR: //Occurs often
SetCursor(NULL);
break;
case WM_SYSCOMMAND: //Occurs often
switch(wParam){
case SC_MONITORPOWER:
case SC_SCREENSAVE:
return 0;
}
break;
case WM_KEYDOWN:
case WM_KEYUP:
System::UserInput.Keys[wParam] = (uMsg == WM_KEYDOWN);
return 0;
case WM_CLOSE:
System::UserInput.CloseWindow = true;
PostQuitMessage(0);
return 0;
case WM_DEVMODECHANGE: {
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
} return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}

View file

@ -18,48 +18,51 @@
#include "../EngineInterface.hpp"
namespace System {
HINSTANCE hInst = NULL;
HANDLE Process;
HANDLE ProcessHeap;
LARGE_INTEGER ClockFreq;
float FramePeriod;
UserInput_t UserInput;
bool SceneFailed = false;
int Initialize(){
memset(&UserInput, 0, sizeof(UserInput));
QueryPerformanceFrequency(&ClockFreq);
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
if(FT_Init_FreeType(&Graphics::FreeTypeLibrary)){
MessageBox(Window::hWnd, "Failed to initialize FreeType.", NULL, MB_OK | MB_ICONERROR);
Graphics::FreeTypeLibrary = NULL;
Shutdown();
return ERROR_SYSTEM_INIT_FREETYPE;
};
if(FT_New_Face(Graphics::FreeTypeLibrary, "simdialogue-uni-game.ttf", 0, &Graphics::FontFace)){
MessageBox(Window::hWnd, "simdialogue-uni-game.ttf does not exist or is corrupt or invalid.",
NULL, MB_OK | MB_ICONERROR);
Graphics::FontFace = NULL;
Shutdown();
return ERROR_SYSTEM_MISSING_FONT;
}
return 0;
}
HINSTANCE hInst = NULL;
HANDLE Process;
HANDLE ProcessHeap;
LARGE_INTEGER ClockFreq;
volatile float FramePeriod;
UserInput_t UserInput;
volatile UserInput_t UserInput_v;
bool SceneFailed = false;
int Initialize(){
memset(&UserInput, 0, sizeof(UserInput));
void Shutdown(){
if(Graphics::FontFace){
FT_Done_Face(Graphics::FontFace);
Graphics::FontFace = NULL;
}
if(Graphics::FreeTypeLibrary){
FT_Done_FreeType(Graphics::FreeTypeLibrary);
Graphics::FreeTypeLibrary = NULL;
}
QueryPerformanceFrequency(&ClockFreq);
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
if(FT_Init_FreeType(&Graphics::FreeTypeLibrary)){
MessageBox(Window::hWnd, "Failed to initialize FreeType.", NULL, MB_OK | MB_ICONERROR);
Graphics::FreeTypeLibrary = NULL;
Shutdown();
return ERROR_SYSTEM_INIT_FREETYPE;
};
if(FT_New_Face(Graphics::FreeTypeLibrary, "simdialogue-uni-game.ttf", 0, &Graphics::FontFace)){
MessageBox(Window::hWnd, "simdialogue-uni-game.ttf does not exist or is corrupt or invalid.",
NULL, MB_OK | MB_ICONERROR);
Graphics::FontFace = NULL;
Shutdown();
return ERROR_SYSTEM_MISSING_FONT;
}
return 0;
}
void Shutdown(){
if(Graphics::FontFace){
FT_Done_Face(Graphics::FontFace);
Graphics::FontFace = NULL;
}
if(Graphics::FreeTypeLibrary){
FT_Done_FreeType(Graphics::FreeTypeLibrary);
Graphics::FreeTypeLibrary = NULL;
}
}
}

View file

@ -15,15 +15,15 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
//System/System.cpp
namespace System {
int Initialize();
void Shutdown();
extern HINSTANCE hInst;
extern HANDLE Process;
extern HANDLE ProcessHeap;
extern LARGE_INTEGER ClockFreq;
extern float FramePeriod;
extern volatile float FramePeriod;
struct UserInput_t {
bool Keys[256];
@ -31,7 +31,8 @@ namespace System {
bool CloseWindow;
};
extern UserInput_t UserInput;
extern volatile UserInput_t UserInput_v;
extern bool SceneFailed;
//Constants
@ -52,8 +53,12 @@ namespace System {
//Use: return ERROR_INIT | result;
#define ERROR_INIT_WINDOW 0x0100
enum {
ERROR_REGISTER_CLASS = 1,
ERROR_CREATE_WINDOW
ERROR_WINDOW_SYNCOBJECT = 1,
ERROR_WINDOW_CREATE_THREAD,
ERROR_WINDOW_SYNCHRONIZE,
ERROR_WINDOW_REGISTER_CLASS,
ERROR_WINDOW_CREATE,
ERROR_WINDOW_HANDLE
};
#define ERROR_INIT_SYSTEM 0x0200
enum {

210
Client/Window/Window.cpp Normal file
View file

@ -0,0 +1,210 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "../EngineInterface.hpp"
namespace Window {
unsigned Width, Height;
bool Fullscreen;
HWND hWnd;
static HANDLE Response = NULL;
static HANDLE Thread = NULL;
static volatile int Result;
static DWORD WINAPI Procedure(LPVOID);
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen);
int Initialize(){
Response = CreateEvent(NULL, FALSE, FALSE, NULL);
if(Response == NULL){
MessageBox(NULL, "Failed to create the message loop synchronization object.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_SYNCOBJECT;
}
Thread = CreateThread(NULL, 1024 /* very tiny stack size is needed */, Window::Procedure, NULL, 0, NULL);
if(Thread == NULL){
MessageBox(NULL, "Failed to create the message loop thread.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_CREATE_THREAD;
}
if(WaitForSingleObject(Window::Response, INFINITE) != WAIT_OBJECT_0){
MessageBox(NULL, "Failed to synchronize with the message loop thread.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_SYNCHRONIZE;
}
if(Result != 0){
Shutdown();
return Result;
}
hWnd = FindWindow("TSO_NIOTSO", "The Sims Online");
if(hWnd == NULL){
MessageBox(NULL, "Failed to obtain a handle for the window.", NULL, MB_OK | MB_ICONERROR);
Shutdown();
return ERROR_WINDOW_HANDLE;
}
return 0;
}
void Shutdown(){
if(hWnd){
DestroyWindow(hWnd);
hWnd = NULL;
}
if(Thread){
DWORD value;
while(GetExitCodeThread(Thread, &value) && value){
//PostQuitMessage() doesn't reliably work from a separate thread
Sleep(1);
}
Thread = NULL;
}
if(Response){
CloseHandle(Response);
Response = NULL;
}
UnregisterClass("TSO_NIOTSO", System::hInst);
}
static LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){
switch(uMsg){
case WM_SETCURSOR:
SetCursor(NULL);
break;
case WM_KEYDOWN:
case WM_KEYUP:
System::UserInput_v.Keys[wParam] = (uMsg == WM_KEYDOWN);
return 0;
case WM_CLOSE:
System::UserInput_v.CloseWindow = true;
return 0;
case WM_SHOWWINDOW:
//Once the window is hidden (we cannot safely destroy the window while we have the OpenGL context),
//WM_QUIT and WM_DESTROY won't show up when a separate thread calls DestroyWindow() or PostQuitMessage().
if(wParam == FALSE) PostQuitMessage(0);
return 0;
case WM_DEVMODECHANGE: {
DEVMODE dm;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm);
System::FramePeriod = 1.0f/dm.dmDisplayFrequency;
} return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
static DWORD WINAPI Procedure(LPVOID){
int result = CreateWindowInvisible(System::hInst, Window::Width, Window::Height, Window::Fullscreen);
if(result != 0){
Window::Result = result;
SetEvent(Window::Response);
return 0;
}
SetEvent(Window::Response);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
static int CreateWindowInvisible(HINSTANCE hInst, unsigned Width, unsigned Height, bool Fullscreen)
{
const WNDCLASS wc = {
CS_OWNDC, //style
WndProc, //lpfnWndProc
0, //cbClsExtra
0, //cbWndExtra
hInst, //hInstance
(HICON) LoadImage(hInst, MAKEINTRESOURCE(IDI_TSO), //hIcon
IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE),
NULL, //hCursor
NULL, //hbrBackground
NULL, //lpszMenuName
"TSO_NIOTSO" //lpszClassName
};
if(!RegisterClass(&wc)){
MessageBox(NULL, "Failed to register the window class.", NULL, MB_OK | MB_ICONERROR);
return ERROR_WINDOW_REGISTER_CLASS;
}
SetCursor(NULL);
HWND hWnd = NULL;
if(Fullscreen){
DEVMODE dmScreenSettings;
dmScreenSettings.dmSize = sizeof(dmScreenSettings);
dmScreenSettings.dmFields = DM_PELSWIDTH|DM_PELSHEIGHT|DM_BITSPERPEL;
dmScreenSettings.dmPelsWidth = Width;
dmScreenSettings.dmPelsHeight = Height;
dmScreenSettings.dmBitsPerPel = 32;
if(ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL){
MessageBox(NULL, "Fullscreen mode is not supported by your setup. It has been turned off.", NULL,
MB_OK | MB_ICONERROR);
Fullscreen = 0;
}else{
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online", WS_POPUP,
0, 0, Width, Height, 0, 0, hInst, NULL);
}
}
if(hWnd == NULL){
Fullscreen = false;
RECT WindowRect = {0, 0, Width, Height};
//Use a style of WS_OVERLAPPEDWINDOW to allow resizing
AdjustWindowRectEx(&WindowRect, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE,
WS_EX_APPWINDOW); //This finds the dimensions of a window with a client area of our specified dimensions
//Note: Windows can be positioned anywhere, even outside the visible workspace,
//but their sizes are limited to the size of the workspace on the primary display.
unsigned WindowWidth = WindowRect.right-WindowRect.left, WindowHeight = WindowRect.bottom-WindowRect.top;
RECT WorkspaceRect;
SystemParametersInfo(SPI_GETWORKAREA, 0, &WorkspaceRect, 0);
hWnd = CreateWindowEx(WS_EX_APPWINDOW, "TSO_NIOTSO", "The Sims Online",
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
((WorkspaceRect.right-WorkspaceRect.left - WindowWidth)>>1) + WorkspaceRect.left,
((WorkspaceRect.bottom-WorkspaceRect.top - WindowHeight)>>1) + WorkspaceRect.top,
WindowWidth, WindowHeight, 0, 0, hInst, NULL);
}
if(hWnd == NULL){
MessageBox(NULL, "Failed to create the window.", NULL, MB_OK | MB_ICONERROR);
return ERROR_WINDOW_CREATE;
}
return 0;
}
}

25
Client/Window/Window.hpp Normal file
View file

@ -0,0 +1,25 @@
/*
Niotso - Copyright (C) 2012 Fatbag <X-Fi6@phppoll.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Window {
int Initialize();
void Shutdown();
extern unsigned Width, Height;
extern bool Fullscreen;
extern HWND hWnd;
}

View file

@ -0,0 +1,221 @@
/*
FileHandler - Copyright (c) 2012 Fatbag <X-Fi6@phppoll.org>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include <memory.h>
#include <stdio.h>
#include <setjmp.h> //Used by libpng
#include "bmp/read_bmp.h"
#include "libjpeg-turbo/jpeglib.h"
#include "libpng/png.h"
#define NOWINDOWS
#include "FileHandler.hpp"
namespace File {
enum SoundType {
FSND_WAV,
FSND_XA,
FSND_UTK,
FSND_MP3,
FSND_COUNT
};
static uint8_t * ReadWAV(Image_t * Image, const uint8_t * InData, size_t FileSize);
static uint8_t * ReadXA(Image_t * Image, const uint8_t * InData, size_t FileSize);
static uint8_t * ReadUTK(Image_t * Image, const uint8_t * InData, size_t FileSize);
static uint8_t * ReadMP3(Image_t * Image, const uint8_t * InData, size_t FileSize);
static const uint8_t Signature[] = {
'R', //WAV
'X', //XA
'U', //UTK
0xFF //MP3
};
static uint8_t* (* const ImageFunction[])(Image_t*, const uint8_t*, size_t) = {
ReadWAV,
ReadXA,
ReadUTK,
ReadMP3
};
Image_t * ReadImageFile(const char * Filename){
uint8_t * InData = File::ReadFile(Filename);
if(InData == NULL) return NULL;
if(File::FileSize < 4){
free(InData);
File::Error = FERR_INVALIDDATA;
return NULL;
}
Image_t * Image = (Image_t*) malloc(sizeof(Image_t));
if(Image == NULL){
free(InData);
File::Error = FERR_MEMORY;
return NULL;
}
uint8_t * OutData = NULL;
for(int i=0; i<FIMG_COUNT; i++){
if(InData[0] == Signature[i]){
OutData = ImageFunction[i](Image, InData, File::FileSize);
free(InData);
if(OutData == NULL){
File::Error = FERR_INVALIDDATA;
return NULL;
}
return Image;
}
}
free(InData);
File::Error = FERR_UNRECOGNIZED;
return NULL;
}
static uint8_t * ReadBMP(Image_t * Image, const uint8_t * InData, size_t FileSize){
bmpheader_t BMPHeader;
if(!bmp_read_header(&BMPHeader, InData, FileSize)){
return NULL;
}
uint8_t * OutData = (uint8_t*) malloc(BMPHeader.DecompressedSize);
if(OutData == NULL){
return NULL;
}
if(!bmp_read_data(&BMPHeader, InData, OutData)){
free(OutData);
return NULL;
}
Image->Width = BMPHeader.biWidth;
Image->Height = BMPHeader.biHeight;
Image->Format = FIMG_BGR24;
Image->Data = OutData;
return OutData;
}
static uint8_t * ReadJPG(Image_t * Image, const uint8_t * InData, size_t FileSize){
//Initialize
jpeg_decompress_struct cinfo;
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, (unsigned char*) InData, FileSize);
if(jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK){
jpeg_destroy_decompress(&cinfo);
return NULL;
}
cinfo.out_color_space = JCS_EXT_BGR;
if(!jpeg_start_decompress(&cinfo)){
jpeg_destroy_decompress(&cinfo);
return NULL;
}
//Read
unsigned row_stride = cinfo.output_width * cinfo.output_components;
uint8_t * OutData = (uint8_t*) malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components);
if(OutData == NULL){
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return NULL;
}
for(unsigned i=cinfo.output_height; i; i--){
//According to the libjpeg documentation,
//jpeg_read_scanlines can only really read 1 scanline at a time.
//We need to convert to bottom-up format anyway.
uint8_t * Location = OutData + (i-1)*row_stride;
if(!jpeg_read_scanlines(&cinfo, &Location, 1)){
free(OutData);
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return NULL;
}
}
//Close up
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
if(Image){
Image->Width = cinfo.output_width;
Image->Height = cinfo.output_height;
Image->Format = FIMG_BGR24;
Image->Data = OutData;
}
return OutData;
}
struct pngdata_t {
const uint8_t * buffer;
size_t size;
};
static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length){
pngdata_t *pngdata = (pngdata_t *) png_get_io_ptr(png_ptr);
if(length > pngdata->size) png_error(png_ptr, "");
memcpy(data, pngdata->buffer, length);
pngdata->buffer += length;
pngdata->size -= length;
}
static uint8_t * ReadPNG(Image_t * Image, const uint8_t * InData, size_t FileSize){
pngdata_t pngdata;
png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if(png_ptr == NULL) return 0;
png_infop info_ptr = png_create_info_struct(png_ptr);
if(info_ptr == NULL){
png_destroy_read_struct(&png_ptr, NULL, NULL);
return NULL;
}
if(setjmp(png_jmpbuf(png_ptr))){
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
pngdata.buffer = InData;
pngdata.size = FileSize;
png_set_read_fn(png_ptr, &pngdata, user_read_data);
png_set_user_limits(png_ptr, 4096, 4096);
png_read_png(png_ptr, info_ptr,
PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA |
PNG_TRANSFORM_PACKING | PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_BGR, NULL);
//png_get_IHDR does not work in high-level mode.
unsigned width = png_get_image_width(png_ptr, info_ptr);
unsigned height = png_get_image_height(png_ptr, info_ptr);
uint8_t * OutData = (uint8_t *) malloc(width*height*3);
if(OutData == NULL){
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
return NULL;
}
uint8_t **Scanlines = png_get_rows(png_ptr, info_ptr);
for(unsigned i=0; i<height; i++)
memcpy(OutData + i*width*3, Scanlines[height-i-1], width*3);
png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
Image->Width = width;
Image->Height = height;
Image->Format = FIMG_BGR24;
Image->Data = OutData;
return OutData;
}
static uint8_t * ReadTGA(Image_t * Image, const uint8_t * InData, size_t FileSize){
return NULL;
}
}

View file

@ -51,6 +51,14 @@ struct Image_t {
uint8_t * Data;
};
struct Audio_t {
unsigned Channels;
unsigned SamplingRate;
unsigned BitDepth;
unsigned Duration;
uint8_t * Data;
};
namespace File {
extern int Error;
@ -58,6 +66,7 @@ extern size_t FileSize;
uint8_t * ReadFile(const char * Filename);
Image_t * ReadImageFile(const char * Filename);
Audio_t * ReadAudioFile(const char * Filename);
}

View file