mirror of
https://github.com/holub/mame
synced 2025-05-29 09:03:08 +03:00
549 lines
13 KiB
C
549 lines
13 KiB
C
//============================================================
|
|
//
|
|
// sdlos_*.c - OS specific low level code
|
|
//
|
|
// Copyright (c) 1996-2010, Nicola Salmoria and the MAME Team.
|
|
// Visit http://mamedev.org for licensing and usage restrictions.
|
|
//
|
|
// SDLMAME by Olivier Galibert and R. Belmont
|
|
//
|
|
//============================================================
|
|
|
|
// standard sdl header
|
|
#include <SDL/SDL.h>
|
|
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
|
|
// MAME headers
|
|
#include "osdcore.h"
|
|
#include "strconv.h"
|
|
|
|
//============================================================
|
|
// PROTOTYPES
|
|
//============================================================
|
|
|
|
static osd_ticks_t init_cycle_counter(void);
|
|
static osd_ticks_t performance_cycle_counter(void);
|
|
|
|
//============================================================
|
|
// STATIC VARIABLES
|
|
//============================================================
|
|
|
|
// global cycle_counter function and divider
|
|
static osd_ticks_t (*cycle_counter)(void) = init_cycle_counter;
|
|
static osd_ticks_t (*ticks_counter)(void) = init_cycle_counter;
|
|
static osd_ticks_t ticks_per_second;
|
|
|
|
//============================================================
|
|
// init_cycle_counter
|
|
//
|
|
// to avoid total grossness, this function is split by subarch
|
|
//============================================================
|
|
|
|
static osd_ticks_t init_cycle_counter(void)
|
|
{
|
|
osd_ticks_t start, end;
|
|
osd_ticks_t a, b;
|
|
int priority = GetThreadPriority(GetCurrentThread());
|
|
LARGE_INTEGER frequency;
|
|
|
|
if (QueryPerformanceFrequency( &frequency ))
|
|
{
|
|
// use performance counter if available as it is constant
|
|
cycle_counter = performance_cycle_counter;
|
|
ticks_counter = performance_cycle_counter;
|
|
|
|
ticks_per_second = frequency.QuadPart;
|
|
|
|
// return the current cycle count
|
|
return (*cycle_counter)();
|
|
}
|
|
else
|
|
{
|
|
fprintf(stderr, "Error! Unable to QueryPerformanceFrequency!\n");
|
|
exit(-1);
|
|
}
|
|
|
|
// temporarily set our priority higher
|
|
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
|
|
|
|
// wait for an edge on the timeGetTime call
|
|
a = SDL_GetTicks();
|
|
do
|
|
{
|
|
b = SDL_GetTicks();
|
|
} while (a == b);
|
|
|
|
// get the starting cycle count
|
|
start = (*cycle_counter)();
|
|
|
|
// now wait for 1/4 second total
|
|
do
|
|
{
|
|
a = SDL_GetTicks();
|
|
} while (a - b < 250);
|
|
|
|
// get the ending cycle count
|
|
end = (*cycle_counter)();
|
|
|
|
// compute ticks_per_sec
|
|
ticks_per_second = (end - start) * 4;
|
|
|
|
// restore our priority
|
|
SetThreadPriority(GetCurrentThread(), priority);
|
|
|
|
// return the current cycle count
|
|
return (*cycle_counter)();
|
|
}
|
|
|
|
//============================================================
|
|
// performance_cycle_counter
|
|
//============================================================
|
|
|
|
static osd_ticks_t performance_cycle_counter(void)
|
|
{
|
|
LARGE_INTEGER performance_count;
|
|
QueryPerformanceCounter(&performance_count);
|
|
return (osd_ticks_t)performance_count.QuadPart;
|
|
}
|
|
|
|
//============================================================
|
|
// osd_cycles
|
|
//============================================================
|
|
|
|
osd_ticks_t osd_ticks(void)
|
|
{
|
|
return (*cycle_counter)();
|
|
}
|
|
|
|
|
|
//============================================================
|
|
// osd_ticks_per_second
|
|
//============================================================
|
|
|
|
osd_ticks_t osd_ticks_per_second(void)
|
|
{
|
|
if (ticks_per_second == 0)
|
|
{
|
|
return 1; // this isn't correct, but it prevents the crash
|
|
}
|
|
return ticks_per_second;
|
|
}
|
|
|
|
//============================================================
|
|
// osd_sleep
|
|
//============================================================
|
|
|
|
void osd_sleep(osd_ticks_t duration)
|
|
{
|
|
UINT32 msec;
|
|
|
|
// make sure we've computed ticks_per_second
|
|
if (ticks_per_second == 0)
|
|
(void)osd_ticks();
|
|
|
|
// convert to milliseconds, rounding down
|
|
msec = (UINT32)(duration * 1000 / ticks_per_second);
|
|
|
|
// only sleep if at least 2 full milliseconds
|
|
if (msec >= 2)
|
|
{
|
|
// take a couple of msecs off the top for good measure
|
|
msec -= 2;
|
|
Sleep(msec);
|
|
}
|
|
}
|
|
|
|
//============================================================
|
|
// osd_num_processors
|
|
//============================================================
|
|
|
|
int osd_num_processors(void)
|
|
{
|
|
SYSTEM_INFO info;
|
|
|
|
// otherwise, fetch the info from the system
|
|
GetSystemInfo(&info);
|
|
|
|
// max out at 4 for now since scaling above that seems to do poorly
|
|
return MIN(info.dwNumberOfProcessors, 4);
|
|
}
|
|
|
|
//============================================================
|
|
// osd_malloc
|
|
//============================================================
|
|
|
|
void *osd_malloc(size_t size)
|
|
{
|
|
#ifndef MALLOC_DEBUG
|
|
return HeapAlloc(GetProcessHeap(), 0, size);
|
|
#else
|
|
// add in space for the base pointer
|
|
size += sizeof(size_t);
|
|
|
|
// small items just come from the heap
|
|
void *result;
|
|
if (size < GUARD_PAGE_THRESH)
|
|
result = HeapAlloc(GetProcessHeap(), 0, size);
|
|
|
|
// large items get guard pages
|
|
else
|
|
{
|
|
// round the size up to a page boundary
|
|
size_t rounded_size = ((size + sizeof(void *) + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE;
|
|
|
|
// reserve that much memory, plus two guard pages
|
|
void *page_base = VirtualAlloc(NULL, rounded_size + 2 * PAGE_SIZE, MEM_RESERVE, PAGE_NOACCESS);
|
|
if (page_base == NULL)
|
|
return NULL;
|
|
|
|
// now allow access to everything but the first and last pages
|
|
page_base = VirtualAlloc(reinterpret_cast<UINT8 *>(page_base) + PAGE_SIZE, rounded_size, MEM_COMMIT, PAGE_READWRITE);
|
|
if (page_base == NULL)
|
|
return NULL;
|
|
|
|
// work backwards from the page base to get to the block base
|
|
result = GUARD_ALIGN_START ? page_base : (reinterpret_cast<UINT8 *>(page_base) + rounded_size - size);
|
|
}
|
|
|
|
// store the page_base at the start
|
|
*reinterpret_cast<size_t *>(result) = size;
|
|
return reinterpret_cast<UINT8 *>(result) + sizeof(size_t);
|
|
#endif
|
|
}
|
|
|
|
|
|
//============================================================
|
|
// osd_free
|
|
//============================================================
|
|
|
|
void osd_free(void *ptr)
|
|
{
|
|
#ifndef MALLOC_DEBUG
|
|
HeapFree(GetProcessHeap(), 0, ptr);
|
|
#else
|
|
size_t size = reinterpret_cast<size_t *>(ptr)[-1];
|
|
|
|
// small items just get freed
|
|
if (size < GUARD_PAGE_THRESH)
|
|
HeapFree(GetProcessHeap(), 0, reinterpret_cast<UINT8 *>(ptr) - sizeof(size_t));
|
|
|
|
// large items need more care
|
|
else
|
|
{
|
|
FPTR page_base = (reinterpret_cast<FPTR>(ptr) - sizeof(size_t)) & ~(PAGE_SIZE - 1);
|
|
VirtualFree(reinterpret_cast<void *>(page_base - PAGE_SIZE), 0, MEM_RELEASE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//============================================================
|
|
// osd_getenv
|
|
//============================================================
|
|
|
|
char *osd_getenv(const char *name)
|
|
{
|
|
return getenv(name);
|
|
}
|
|
|
|
//============================================================
|
|
// osd_setenv
|
|
//============================================================
|
|
|
|
int osd_setenv(const char *name, const char *value, int overwrite)
|
|
{
|
|
char *buf;
|
|
int result;
|
|
|
|
if (!overwrite)
|
|
{
|
|
if (osd_getenv(name) != NULL)
|
|
return 0;
|
|
}
|
|
buf = (char *) osd_malloc(strlen(name)+strlen(value)+2);
|
|
sprintf(buf, "%s=%s", name, value);
|
|
result = putenv(buf);
|
|
|
|
/* will be referenced by environment
|
|
* Therefore it is not freed here
|
|
*/
|
|
|
|
return result;
|
|
}
|
|
|
|
//============================================================
|
|
// get_clipboard_text_by_format
|
|
//============================================================
|
|
|
|
static char *get_clipboard_text_by_format(UINT format, char *(*convert)(LPCVOID data))
|
|
{
|
|
char *result = NULL;
|
|
HANDLE data_handle;
|
|
LPVOID data;
|
|
|
|
// check to see if this format is available
|
|
if (IsClipboardFormatAvailable(format))
|
|
{
|
|
// open the clipboard
|
|
if (OpenClipboard(NULL))
|
|
{
|
|
// try to access clipboard data
|
|
data_handle = GetClipboardData(format);
|
|
if (data_handle != NULL)
|
|
{
|
|
// lock the data
|
|
data = GlobalLock(data_handle);
|
|
if (data != NULL)
|
|
{
|
|
// invoke the convert
|
|
result = (*convert)(data);
|
|
|
|
// unlock the data
|
|
GlobalUnlock(data_handle);
|
|
}
|
|
}
|
|
|
|
// close out the clipboard
|
|
CloseClipboard();
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
//============================================================
|
|
// convert_wide
|
|
//============================================================
|
|
|
|
static char *convert_wide(LPCVOID data)
|
|
{
|
|
return utf8_from_wstring((LPCWSTR) data);
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// convert_ansi
|
|
//============================================================
|
|
|
|
static char *convert_ansi(LPCVOID data)
|
|
{
|
|
return utf8_from_astring((LPCSTR) data);
|
|
}
|
|
|
|
|
|
|
|
//============================================================
|
|
// osd_get_clipboard_text
|
|
//============================================================
|
|
|
|
char *osd_get_clipboard_text(void)
|
|
{
|
|
char *result = NULL;
|
|
|
|
// try to access unicode text
|
|
if (result == NULL)
|
|
result = get_clipboard_text_by_format(CF_UNICODETEXT, convert_wide);
|
|
|
|
// try to access ANSI text
|
|
if (result == NULL)
|
|
result = get_clipboard_text_by_format(CF_TEXT, convert_ansi);
|
|
|
|
return result;
|
|
}
|
|
|
|
//============================================================
|
|
// astring_from_utf8
|
|
//============================================================
|
|
|
|
CHAR *astring_from_utf8(const char *utf8string)
|
|
{
|
|
WCHAR *wstring;
|
|
int char_count;
|
|
CHAR *result;
|
|
|
|
// convert MAME string (UTF-8) to UTF-16
|
|
char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
|
|
wstring = (WCHAR *)alloca(char_count * sizeof(*wstring));
|
|
MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, wstring, char_count);
|
|
|
|
// convert UTF-16 to "ANSI code page" string
|
|
char_count = WideCharToMultiByte(CP_ACP, 0, wstring, -1, NULL, 0, NULL, NULL);
|
|
result = (CHAR *)osd_malloc(char_count * sizeof(*result));
|
|
if (result != NULL)
|
|
WideCharToMultiByte(CP_ACP, 0, wstring, -1, result, char_count, NULL, NULL);
|
|
|
|
return result;
|
|
}
|
|
|
|
//============================================================
|
|
// wstring_from_utf8
|
|
//============================================================
|
|
|
|
WCHAR *wstring_from_utf8(const char *utf8string)
|
|
{
|
|
int char_count;
|
|
WCHAR *result;
|
|
|
|
// convert MAME string (UTF-8) to UTF-16
|
|
char_count = MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, NULL, 0);
|
|
result = (WCHAR *)osd_malloc(char_count * sizeof(*result));
|
|
if (result != NULL)
|
|
MultiByteToWideChar(CP_UTF8, 0, utf8string, -1, result, char_count);
|
|
|
|
return result;
|
|
}
|
|
|
|
//============================================================
|
|
// win_attributes_to_entry_type
|
|
//============================================================
|
|
|
|
static osd_dir_entry_type win_attributes_to_entry_type(DWORD attributes)
|
|
{
|
|
if (attributes == 0xFFFFFFFF)
|
|
return ENTTYPE_NONE;
|
|
else if (attributes & FILE_ATTRIBUTE_DIRECTORY)
|
|
return ENTTYPE_DIR;
|
|
else
|
|
return ENTTYPE_FILE;
|
|
}
|
|
|
|
//============================================================
|
|
// osd_stat
|
|
//============================================================
|
|
|
|
osd_directory_entry *osd_stat(const char *path)
|
|
{
|
|
osd_directory_entry *result = NULL;
|
|
TCHAR *t_path;
|
|
HANDLE find = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATA find_data;
|
|
|
|
// convert the path to TCHARs
|
|
t_path = tstring_from_utf8(path);
|
|
if (t_path == NULL)
|
|
goto done;
|
|
|
|
// attempt to find the first file
|
|
find = FindFirstFile(t_path, &find_data);
|
|
if (find == INVALID_HANDLE_VALUE)
|
|
goto done;
|
|
|
|
// create an osd_directory_entry; be sure to make sure that the caller can
|
|
// free all resources by just freeing the resulting osd_directory_entry
|
|
result = (osd_directory_entry *) malloc(sizeof(*result) + strlen(path) + 1);
|
|
if (!result)
|
|
goto done;
|
|
strcpy(((char *) result) + sizeof(*result), path);
|
|
result->name = ((char *) result) + sizeof(*result);
|
|
result->type = win_attributes_to_entry_type(find_data.dwFileAttributes);
|
|
result->size = find_data.nFileSizeLow | ((UINT64) find_data.nFileSizeHigh << 32);
|
|
|
|
done:
|
|
if (t_path)
|
|
osd_free(t_path);
|
|
return result;
|
|
}
|
|
|
|
//============================================================
|
|
// osd_get_volume_name
|
|
//============================================================
|
|
|
|
const char *osd_get_volume_name(int idx)
|
|
{
|
|
static char szBuffer[128];
|
|
const char *p;
|
|
|
|
GetLogicalDriveStringsA(ARRAY_LENGTH(szBuffer), szBuffer);
|
|
|
|
p = szBuffer;
|
|
while(idx--) {
|
|
p += strlen(p) + 1;
|
|
if (!*p) return NULL;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
//============================================================
|
|
// win_error_to_mame_file_error
|
|
//============================================================
|
|
|
|
static file_error win_error_to_mame_file_error(DWORD error)
|
|
{
|
|
file_error filerr;
|
|
|
|
// convert a Windows error to a file_error
|
|
switch (error)
|
|
{
|
|
case ERROR_SUCCESS:
|
|
filerr = FILERR_NONE;
|
|
break;
|
|
|
|
case ERROR_OUTOFMEMORY:
|
|
filerr = FILERR_OUT_OF_MEMORY;
|
|
break;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
filerr = FILERR_NOT_FOUND;
|
|
break;
|
|
|
|
case ERROR_ACCESS_DENIED:
|
|
filerr = FILERR_ACCESS_DENIED;
|
|
break;
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
filerr = FILERR_ALREADY_OPEN;
|
|
break;
|
|
|
|
default:
|
|
filerr = FILERR_FAILURE;
|
|
break;
|
|
}
|
|
return filerr;
|
|
}
|
|
|
|
//============================================================
|
|
// osd_get_full_path
|
|
//============================================================
|
|
|
|
file_error osd_get_full_path(char **dst, const char *path)
|
|
{
|
|
file_error err;
|
|
TCHAR *t_path;
|
|
TCHAR buffer[MAX_PATH];
|
|
|
|
// convert the path to TCHARs
|
|
t_path = tstring_from_utf8(path);
|
|
if (t_path == NULL)
|
|
{
|
|
err = FILERR_OUT_OF_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
// cannonicalize the path
|
|
if (!GetFullPathName(t_path, ARRAY_LENGTH(buffer), buffer, NULL))
|
|
{
|
|
err = win_error_to_mame_file_error(GetLastError());
|
|
goto done;
|
|
}
|
|
|
|
// convert the result back to UTF-8
|
|
*dst = utf8_from_tstring(buffer);
|
|
if (!*dst)
|
|
{
|
|
err = FILERR_OUT_OF_MEMORY;
|
|
goto done;
|
|
}
|
|
|
|
err = FILERR_NONE;
|
|
|
|
done:
|
|
if (t_path != NULL)
|
|
osd_free(t_path);
|
|
return err;
|
|
}
|