Bemanitools v5.26 release

This commit is contained in:
icex2
2019-09-27 22:36:50 +02:00
commit cbd7720349
718 changed files with 62731 additions and 0 deletions
+21
View File
@@ -0,0 +1,21 @@
libs += util
src_util := \
array.c \
cmdline.c \
crc.c \
crypto.c \
fs.c \
hex.c \
hr.c \
iobuf.c \
list.c \
log.c \
mem.c \
msg-thread.c \
net.c \
str.c \
thread.c \
time.c \
winres.c \
+58
View File
@@ -0,0 +1,58 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "util/array.h"
#include "util/log.h"
#include "util/mem.h"
void array_init(struct array *array)
{
memset(array, 0, sizeof(*array));
}
void array_remove_(size_t itemsz, struct array *array, size_t i)
{
log_assert(i < array->nitems);
array->nitems--;
memmove(((uint8_t *) array->items) + i * itemsz,
((uint8_t *) array->items) + (i + 1) * itemsz,
(array->nitems - i) * itemsz);
}
void *array_reserve_(size_t itemsz, struct array *array, size_t nitems)
{
size_t new_nalloced;
size_t new_nitems;
void *result;
new_nitems = array->nitems + nitems;
if (new_nitems > array->nalloced) {
new_nalloced = array->nalloced;
if (new_nalloced == 0) {
new_nalloced = 1;
}
while (new_nalloced < new_nitems) {
new_nalloced *= 2;
}
array->items = xrealloc(array->items, new_nalloced * itemsz);
array->nalloced = new_nalloced;
}
result = ((uint8_t *) array->items) + array->nitems * itemsz;
array->nitems = new_nitems;
return result;
}
void array_fini(struct array *array)
{
free(array->items);
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef UTIL_ARRAY_H
#define UTIL_ARRAY_H
#include <stddef.h>
struct array {
void *items;
size_t nitems;
size_t nalloced;
};
#define array_item(type, array, i) \
(&(((type *) (array)->items)[i]))
#define array_reserve(type, array, nitems) \
((type *) array_reserve_(sizeof(type), array, nitems))
#define array_remove(type, array, i) \
array_remove_(sizeof(type), array, i)
#define array_append(type, array) \
array_reserve(type, array, 1)
void array_init(struct array *array);
void *array_reserve_(size_t itemsz, struct array *array, size_t nitems);
void array_remove_(size_t itemsz, struct array *array, size_t i);
void array_fini(struct array *array);
#endif
+129
View File
@@ -0,0 +1,129 @@
#include "util/cmdline.h"
#include "util/mem.h"
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
static void push_argv(int *argc, char ***argv, const char *begin,
const char *end)
{
size_t nchars;
char *str;
(*argc)++;
*argv = xrealloc(*argv, *argc * sizeof(char **));
nchars = end - begin;
str = xmalloc(nchars + 1);
memcpy(str, begin, nchars);
str[nchars] = '\0';
(*argv)[*argc - 1] = str;
}
void args_recover(int *argc_out, char ***argv_out)
{
int argc;
char **argv;
const char *begin;
const char *pos;
bool quote;
argc = 0;
argv = NULL;
quote = false;
for (begin = pos = GetCommandLine() ; *pos ; pos++) {
switch (*pos) {
case '"':
if (!quote) {
quote = true;
begin = pos + 1;
} else {
push_argv(&argc, &argv, begin, pos);
quote = false;
begin = NULL;
}
break;
case ' ':
if (!quote && begin != NULL) {
push_argv(&argc, &argv, begin, pos);
begin = NULL;
}
break;
default:
if (begin == NULL) {
begin = pos;
}
break;
}
}
if (begin != NULL && !quote) {
push_argv(&argc, &argv, begin, pos);
}
*argc_out = argc;
*argv_out = argv;
}
void args_free(int argc, char **argv)
{
int i;
for (i = 0 ; i < argc ; i++) {
free(argv[i]);
}
free(argv);
}
char *args_join(int argc, char **argv)
{
char *pos;
char *str;
uint32_t nchars;
uint32_t part_len;
int i;
nchars = 0;
for (i = 0 ; i < argc ; i++) {
/* 3 = leading space plus two surrounding quotes.
The first element has no leading space, but this is counterbalanced
by the fact that the string needs a NUL terminator. */
nchars += 3 + strlen(argv[i]);
}
str = xmalloc(nchars);
pos = str;
for (i = 0 ; i < argc ; i++) {
if (i != 0) {
*pos++ = ' ';
}
*pos++ = '"';
part_len = strlen(argv[i]);
memcpy(pos, argv[i], part_len);
pos += part_len;
*pos++ = '"';
}
*pos = '\0';
return str;
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef UTIL_CMDLINE_H
#define UTIL_CMDLINE_H
void args_recover(int *argc, char ***argv);
void args_free(int argc, char **argv);
char *args_join(int argc, char **argv);
#endif
+6
View File
@@ -0,0 +1,6 @@
#ifndef UTIL_CODEPAGE_H
#define UTIL_CODEPAGE_H
#define CP_SHIFT_JIS 932
#endif
+83
View File
@@ -0,0 +1,83 @@
#include <stddef.h>
#include <stdint.h>
uint8_t crc8(const void *ptr, size_t nbytes, uint8_t in)
{
const uint8_t *bytes;
uint8_t crc;
size_t i;
size_t j;
//log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;
for (i = 0 ; i < nbytes ; i++) {
crc ^= bytes[i];
for (j = 0 ; j < 8 ; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0x8C;
} else {
crc = crc >> 1;
}
}
}
return ~crc;
}
uint16_t crc16(const void *ptr, size_t nbytes, uint16_t in)
{
const uint8_t *bytes;
uint16_t crc;
size_t i;
size_t j;
//log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;
for (i = 0 ; i < nbytes ; i++) {
crc ^= bytes[i];
for (j = 0 ; j < 8 ; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0x8408;
} else {
crc = crc >> 1;
}
}
}
return ~crc;
}
uint32_t crc32(const void *ptr, size_t nbytes, uint32_t in)
{
const uint8_t *bytes;
uint32_t crc;
size_t i;
size_t j;
//log_assert(ptr != NULL);
bytes = ptr;
crc = ~in;
for (i = 0 ; i < nbytes ; i++) {
crc ^= bytes[i];
for (j = 0 ; j < 8 ; j++) {
if (crc & 1) {
crc = (crc >> 1) ^ 0xEDB88320;
} else {
crc = (crc >> 1);
}
}
}
return ~crc;
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef UTIL_CRC_H
#define UTIL_CRC_H
#include <stddef.h>
#include <stdint.h>
uint8_t crc8(const void *ptr, size_t nbytes, uint8_t in);
uint16_t crc16(const void *ptr, size_t nbytes, uint16_t in);
uint32_t crc32(const void *ptr, size_t nbytes, uint32_t in);
#endif
+262
View File
@@ -0,0 +1,262 @@
#define LOG_MODULE "crypto"
#include "util/crypto.h"
#include "util/log.h"
#include "util/mem.h"
#include <stdlib.h>
#include <string.h>
static const char vista_prov[]
= "Microsoft Enhanced RSA and AES Cryptographic Provider";
static const char winxp_prov[]
= "Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)";
static int init_count;
static HCRYPTPROV prov;
void crypto_init(void)
{
const char *prov_name;
OSVERSIONINFOEX osix;
if (init_count++ != 0) {
return;
}
log_misc("Initializing win32 crypto wrappers");
memset(&osix, 0, sizeof(osix));
osix.dwOSVersionInfoSize = sizeof(osix);
GetVersionEx((void *) &osix);
if (osix.dwMajorVersion < 6) {
prov_name = winxp_prov;
} else {
prov_name = vista_prov;
}
if (!CryptAcquireContext(&prov, NULL, prov_name, PROV_RSA_AES,
CRYPT_VERIFYCONTEXT)) {
log_fatal("CryptAcquireContext failed: %08x",
(unsigned int) GetLastError());
}
}
void crypto_gen_random(void *bytes, size_t nbytes)
{
CryptGenRandom(prov, nbytes, bytes);
}
void crypto_fini(void)
{
if (--init_count != 0) {
return;
}
log_misc("Shutting down win32 crypto wrappers");
if (!CryptReleaseContext(prov, 0)) {
log_fatal("CryptReleaseContext failed: %08x",
(unsigned int) GetLastError());
}
}
void md5_compute(const void *in_bytes, size_t in_nbytes, uint8_t *out_bytes,
size_t out_nbytes)
{
struct md5 md5;
md5_init(&md5);
md5_append(&md5, in_bytes, in_nbytes);
md5_fini(&md5, out_bytes, out_nbytes);
}
void md5_init(struct md5 *md5)
{
if (!CryptCreateHash(prov, CALG_MD5, 0, 0, &md5->hash)) {
log_fatal("CryptCreateHash(CALG_MD5) failed: %08x",
(unsigned int) GetLastError());
}
}
void md5_append(struct md5 *md5, const void *bytes, size_t nbytes)
{
if (!CryptHashData(md5->hash, bytes, nbytes, 0)) {
log_fatal("CryptHashData failed: %08x",
(unsigned int) GetLastError());
}
}
void md5_fini(struct md5 *md5, uint8_t *output, size_t nbytes)
{
DWORD tmp;
log_assert(output != NULL && nbytes == 16);
tmp = nbytes;
if (output != NULL) {
if (!CryptGetHashParam(md5->hash, HP_HASHVAL, output, &tmp, 0)) {
log_fatal("Error getting MD5 result: %08x",
(unsigned int) GetLastError());
}
}
if (!CryptDestroyHash(md5->hash)) {
log_fatal("CryptDestroyHash failed: %08x",
(unsigned int) GetLastError());
}
}
#define SWAP(x, y, tmp) tmp = x; x = y; y = tmp
void arc4_init(struct arc4 *arc4, const uint8_t *key, size_t key_nbytes)
{
size_t i;
uint8_t j;
uint8_t tmp;
arc4->i = 0;
arc4->j = 0;
for (i = 0 ; i < 256 ; i++) {
arc4->S[i] = (uint8_t) i;
}
j = 0;
for (i = 0 ; i < 256 ; i++) {
j = j + arc4->S[i] + key[i % key_nbytes];
SWAP(arc4->S[i], arc4->S[j], tmp);
}
}
void arc4_apply(struct arc4 *arc4, uint8_t *bytes, size_t nbytes)
{
size_t i;
uint8_t tmp;
for (i = 0 ; i < nbytes ; i++) {
arc4->i += 1;
arc4->j += arc4->S[arc4->i];
SWAP(arc4->S[arc4->i], arc4->S[arc4->j], tmp);
bytes[i] ^= arc4->S[(uint8_t) (arc4->S[arc4->i] + arc4->S[arc4->j])];
}
}
static uint32_t blowfish_F(struct blowfish* blowfish, uint32_t x)
{
unsigned short a, b, c, d;
uint32_t y;
d = x & 0x00FF;
x >>= 8;
c = x & 0x00FF;
x >>= 8;
b = x & 0x00FF;
x >>= 8;
a = x & 0x00FF;
y = blowfish->S[0][a] + blowfish->S[1][b];
y = y ^ blowfish->S[2][c];
y = y + blowfish->S[3][d];
return y;
}
void blowfish_init(struct blowfish* blowfish, const uint8_t* key,
size_t key_length)
{
int i, j, k;
uint32_t data, datal, datar;
j = 0;
for (i = 0; i < 16 + 2; ++i) {
data = 0x00000000;
for (k = 0; k < 4; ++k) {
data = (data << 8) | key[j];
j = j + 1;
if (j >= key_length)
j = 0;
}
blowfish->P[i] = blowfish->P[i] ^ data;
}
datal = 0x00000000;
datar = 0x00000000;
for (i = 0; i < 16 + 2; i += 2) {
blowfish_encrypt(blowfish, &datal, &datar);
blowfish->P[i] = datal;
blowfish->P[i + 1] = datar;
}
for (i = 0; i < 4; ++i) {
for (j = 0; j < 256; j += 2) {
blowfish_encrypt(blowfish, &datal, &datar);
blowfish->S[i][j] = datal;
blowfish->S[i][j + 1] = datar;
}
}
}
void blowfish_encrypt(struct blowfish* blowfish, uint32_t* xl, uint32_t* xr)
{
uint32_t Xl;
uint32_t Xr;
uint32_t temp;
short i;
Xl = *xl;
Xr = *xr;
for (i = 0; i < 16; ++i) {
Xl = Xl ^ blowfish->P[i];
Xr = blowfish_F(blowfish, Xl) ^ Xr;
temp = Xl;
Xl = Xr;
Xr = temp;
}
temp = Xl;
Xl = Xr;
Xr = temp;
Xr = Xr ^ blowfish->P[16];
Xl = Xl ^ blowfish->P[16 + 1];
*xl = Xl;
*xr = Xr;
}
void blowfish_decrypt(struct blowfish* blowfish, uint32_t* xl, uint32_t* xr)
{
uint32_t Xl;
uint32_t Xr;
uint32_t temp;
short i;
Xl = *xl;
Xr = *xr;
for (i = 16 + 1; i > 1; --i) {
Xl = Xl ^ blowfish->P[i];
Xr = blowfish_F(blowfish, Xl) ^ Xr;
/* Exchange Xl and Xr */
temp = Xl;
Xl = Xr;
Xr = temp;
}
/* Exchange Xl and Xr */
temp = Xl;
Xl = Xr;
Xr = temp;
Xr = Xr ^ blowfish->P[1];
Xl = Xl ^ blowfish->P[0];
*xl = Xl;
*xr = Xr;
}
+42
View File
@@ -0,0 +1,42 @@
#ifndef UTIL_CRYPTO_H
#define UTIL_CRYPTO_H
#include <windows.h>
#include <wincrypt.h>
#include <stddef.h>
#include <stdint.h>
struct arc4 {
uint8_t S[256];
uint8_t i;
uint8_t j;
};
struct md5 {
HCRYPTHASH hash;
};
struct blowfish {
uint32_t P[16 + 2];
uint32_t S[4][256];
};
void crypto_init(void);
void crypto_gen_random(void *bytes, size_t nbytes);
void crypto_fini(void);
void md5_compute(const void *in_bytes, size_t in_nbytes, uint8_t *out_bytes,
size_t out_nbytes);
void md5_init(struct md5 *md5);
void md5_append(struct md5 *md5, const void *bytes, size_t nbytes);
void md5_fini(struct md5 *md5, uint8_t *output, size_t nbytes);
void arc4_init(struct arc4 *arc4, const uint8_t *key, size_t key_nbytes);
void arc4_apply(struct arc4 *arc4, uint8_t *bytes, size_t nbytes);
void blowfish_init(struct blowfish* blowfish, const uint8_t* key, size_t key_length);
void blowfish_encrypt(struct blowfish* blowfish, uint32_t* xl, uint32_t* xr);
void blowfish_decrypt(struct blowfish* blowfish, uint32_t* xl, uint32_t* xr);
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef UTIL_DEFS_H
#define UTIL_DEFS_H
#include <stdint.h>
#define STRINGIFY_(x) #x
#define STRINGIFY(x) STRINGIFY_(x)
#define DLLEXPORT __declspec(dllexport)
#define DLLIMPORT __declspec(dllimport)
#define STDCALL __stdcall
#define lengthof(x) (sizeof(x) / sizeof(x[0]))
#define containerof(ptr, outer_t, member) \
((void *) (((uint8_t *) ptr) - offsetof(outer_t, member)))
#endif
+267
View File
@@ -0,0 +1,267 @@
#include <windows.h>
#include <shlobj.h>
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include "util/defs.h"
#include "util/fs.h"
#include "util/log.h"
#include "util/mem.h"
#include "util/str.h"
bool file_load(const char *filename, void **out_bytes, size_t *out_nbytes,
bool text_mode)
{
FILE *f;
void *bytes;
size_t nbytes;
int result;
log_assert(out_bytes != NULL);
*out_bytes = NULL;
f = fopen(filename, "rb");
if (f == NULL) {
log_warning("%s: Error opening file: %s", filename, strerror(errno));
goto open_fail;
}
fseek(f, 0, SEEK_END);
nbytes = ftell(f);
fseek(f, 0, SEEK_SET);
/* Add for null terminator */
if (text_mode) {
nbytes++;
}
bytes = malloc(nbytes);
if (bytes == NULL) {
log_warning("%s: malloc(%u) failed", filename, (unsigned int) nbytes);
goto malloc_fail;
}
if (text_mode) {
result = fread(bytes, nbytes - 1, 1, f);
} else {
result = fread(bytes, nbytes, 1, f);
}
if (result != 1) {
log_warning("%s: Error reading file: %s", filename, strerror(errno));
goto read_fail;
}
*out_bytes = bytes;
if (out_nbytes != NULL) {
*out_nbytes = nbytes;
}
fclose(f);
/* Add null terminator */
if (text_mode) {
((char*) bytes)[nbytes - 1] = '\0';
}
log_misc("File loaded %s, size %lu", filename,
(long unsigned int) *out_nbytes);
return true;
read_fail:
free(bytes);
malloc_fail:
fclose(f);
open_fail:
return false;
}
bool file_save(const char *filename, const void *bytes, size_t nbytes)
{
FILE *f;
int result;
f = fopen(filename, "wb");
if (f == NULL) {
log_warning("%s: Error creating file: %s", filename, strerror(errno));
goto open_fail;
}
result = fwrite(bytes, nbytes, 1, f);
if (result != 1) {
log_warning("%s: Error writing file: %s", filename, strerror(errno));
goto write_fail;
}
fclose(f);
return true;
write_fail:
fclose(f);
open_fail:
return false;
}
bool path_exists(const char* path)
{
DWORD attrib = GetFileAttributes(path);
return attrib != INVALID_FILE_ATTRIBUTES;
}
bool path_exists_wstr(const wchar_t* path)
{
DWORD attrib = GetFileAttributesW(path);
return attrib != INVALID_FILE_ATTRIBUTES;
}
FILE *fopen_appdata(const char *vendor, const char *filename,
const char *mode)
{
char root[MAX_PATH];
char path[MAX_PATH];
HRESULT hr;
FILE *f;
hr = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, root);
if (FAILED(hr)) {
log_warning("Failed to get AppData folder: hr=%08x",
(unsigned int) hr);
return NULL;
}
str_format(path, sizeof(path), "%s\\%s", root, vendor);
if (!CreateDirectory(path, NULL)
&& GetLastError() != ERROR_ALREADY_EXISTS) {
log_warning("Failed to create %s: %08x",
path, (unsigned int) GetLastError());
return NULL;
}
str_format(path, sizeof(path), "%s\\%s\\%s", root, vendor, filename);
f = fopen(path, mode);
if (f == NULL) {
log_warning("fopen(\"%s\", \"%s\") failed: %s",
path, mode, strerror(errno));
}
return f;
}
bool path_mkdir(const char *path)
{
char buf[MAX_PATH];
char *pos;
str_cpy(buf, lengthof(buf), path);
for (pos = path_next_element(buf)
; pos
; pos = path_next_element(pos + 1)) {
*pos = '\0';
if (strlen(buf) != 2 || buf[1] != ':') {
CreateDirectory(buf, NULL);
if (GetLastError() != ERROR_ALREADY_EXISTS) {
log_warning("%s: Cannot create directory: %#x",
buf, (unsigned int) GetLastError());
return false;
}
}
*pos = '/';
}
CreateDirectory(buf, NULL);
if (GetLastError() != ERROR_ALREADY_EXISTS) {
log_warning("%s: Cannot create directory: %#x",
buf, (unsigned int) GetLastError());
return false;
}
return true;
}
char *path_next_element(char *path)
{
char *c;
for (c = path ; *c ; c++) {
if (*c == '/' || *c == '\\') {
return c;
}
}
return NULL;
}
bool read_str(FILE *f, char **str)
{
uint32_t len;
char *buf;
if (fread(&len, sizeof(len), 1, f) != 1) {
goto len_fail;
}
buf = malloc(len + 1);
if (buf == NULL) {
goto alloc_fail;
}
if (len > 0 && fread(buf, len, 1, f) != 1) {
goto body_fail;
}
buf[len] = '\0';
*str = buf;
return true;
body_fail:
free(buf);
alloc_fail:
len_fail:
return false;
}
void write_str(FILE *f, const char *str)
{
uint32_t len;
len = strlen(str);
fwrite(&len, sizeof(len), 1, f);
fwrite(str, len, 1, f);
}
+75
View File
@@ -0,0 +1,75 @@
#ifndef UTIL_FS_H
#define UTIL_FS_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
/**
* Load a file into memory
*
* @param filename Path of the file to load
* @param bytes Pointer to a pointer to return an allocated location with the
* loaded data to
* @param nbytes Pointer to a variable to return the read size of the file to
* @param text_mode True to read the file in text mode (add terminating \0),
* false for binary mode
* @return True on success, false on error
*/
bool file_load(const char *filename, void **bytes, size_t *nbytes,
bool text_mode);
/**
* Save a buffer to a file (overwrite any existing files with the same name)
*
* @param filename Path to save the data to
* @param bytes Pointer to buffer with contents to save
* @param nbytes Number of bytes to write to the file
* @return True on success, false on error
*/
bool file_save(const char *filename, const void *bytes, size_t nbytes);
/**
* Check if a normal file, folder, symlink, ... exists
*
* @param path Path as c-string.
* @return True if exists, false otherwise
*/
bool path_exists(const char* path);
/**
* Check if a normal file, folder, symlink, ... exists
*
* @param path Path as w-string.
* @return True if exists, false otherwise
*/
bool path_exists_wstr(const wchar_t* path);
FILE *fopen_appdata(const char *vendor, const char *filename,
const char *mode);
/**
* Create a new directory
*
* @param path Path to directory
* @return True if successful, false on failure
*/
bool path_mkdir(const char *path);
char *path_next_element(char *path);
#define read_bin(f, val, size) (fread((val), (size), 1, (f)) == 1)
#define read_u8(f, val) (fread((val), 1, 1, (f)) == 1)
#define read_u16(f, val) (fread((val), 2, 1, (f)) == 1)
#define read_u32(f, val) (fread((val), 4, 1, (f)) == 1)
bool read_str(FILE *f, char **str);
#define write_bin(f, val, size) fwrite((val), (size), 1, (f))
#define write_u8(f, val) fwrite((val), 1, 1, (f))
#define write_u16(f, val) fwrite((val), 2, 1, (f))
#define write_u32(f, val) fwrite((val), 4, 1, (f))
void write_str(FILE *f, const char *str);
#endif
+72
View File
@@ -0,0 +1,72 @@
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include "util/hex.h"
#include "util/log.h"
static bool hex_decode_nibble(char c, uint8_t *nibble)
{
if (c >= '0' && c <= '9') {
*nibble = c - '0';
} else if (c >= 'A' && c <= 'F') {
*nibble = c - 'A' + 10;
} else if (c >= 'a' && c <= 'f') {
*nibble = c - 'a' + 10;
} else {
return false;
}
return true;
}
bool hex_decode(void *ptr, size_t nbytes, const char *chars, size_t nchars)
{
uint8_t *bytes = ptr;
uint32_t i;
uint8_t hi;
uint8_t lo;
log_assert(nchars <= 2 * nbytes);
log_assert(nchars % 2 == 0);
for (i = 0 ; i < nchars / 2; i++) {
if ( !hex_decode_nibble(chars[2 * i + 0], &hi) ||
!hex_decode_nibble(chars[2 * i + 1], &lo)) {
return false;
}
bytes[i] = (hi << 4) | lo;
}
return true;
}
static void hex_encode(const void *ptr, size_t nbytes, char *chars,
size_t nchars, const char *digits)
{
const uint8_t *bytes = ptr;
size_t i;
log_assert(nchars >= 2 * nbytes + 1);
for (i = 0 ; i < nbytes ; i++) {
chars[i * 2 + 0] = digits[bytes[i] >> 4];
chars[i * 2 + 1] = digits[bytes[i] & 15];
}
chars[nbytes * 2] = '\0';
}
void hex_encode_lc(const void *bytes, size_t nbytes, char *chars, size_t nchars)
{
hex_encode(bytes, nbytes, chars, nchars, "0123456789abcdef");
}
void hex_encode_uc(const void *bytes, size_t nbytes, char *chars, size_t nchars)
{
hex_encode(bytes, nbytes, chars, nchars, "0123456789ABCDEF");
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef UTIL_HEX_H
#define UTIL_HEX_H
#include <stdbool.h>
#include <stddef.h>
bool hex_decode(void *bytes, size_t nbytes, const char *chars,
size_t nchars);
void hex_encode_uc(const void *bytes, size_t nbytes, char *chars,
size_t nchars);
void hex_encode_lc(const void *bytes, size_t nbytes, char *chars,
size_t nchars);
#endif
+39
View File
@@ -0,0 +1,39 @@
#include <windows.h>
#include <stdint.h>
#include "util/hr.h"
HRESULT hr_from_win32(void)
{
return HRESULT_FROM_WIN32(GetLastError());
}
void hr_propagate_win32_(HRESULT hr)
{
uint32_t result;
if (SUCCEEDED(hr)) {
result = ERROR_SUCCESS;
} else if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
result = HRESULT_CODE(hr);
} else {
// https://docs.microsoft.com/en-us/windows/desktop/seccrypto/common-hresult-values
switch (hr) {
case E_ABORT: result = ERROR_OPERATION_ABORTED; break;
case E_ACCESSDENIED: result = ERROR_ACCESS_DENIED; break;
case E_FAIL: result = ERROR_GEN_FAILURE; break;
case E_HANDLE: result = ERROR_INVALID_HANDLE; break;
case E_INVALIDARG: result = ERROR_INVALID_PARAMETER; break;
case E_NOINTERFACE: result = ERROR_NOT_SUPPORTED; break;
case E_NOTIMPL: result = ERROR_NOT_SUPPORTED; break;
case E_OUTOFMEMORY: result = ERROR_OUTOFMEMORY; break;
case E_POINTER: result = ERROR_INVALID_ADDRESS; break;
case E_UNEXPECTED: result = ERROR_INTERNAL_ERROR; break;
default: result = ERROR_INTERNAL_ERROR; break;
}
}
SetLastError(result);
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef UTIL_HR_H
#define UTIL_HR_H
#include <windows.h>
#define hr_propagate_win32(hr, r) (hr_propagate_win32_(hr), r)
HRESULT hr_from_win32(void);
void hr_propagate_win32_(HRESULT hr);
#endif
+72
View File
@@ -0,0 +1,72 @@
#define LOG_MODULE "iobuf"
#include <string.h>
#include "util/hex.h"
#include "util/iobuf.h"
#include "util/log.h"
#include "util/mem.h"
size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src)
{
size_t dest_avail;
size_t src_avail;
size_t chunksz;
dest_avail = dest->nbytes - dest->pos;
src_avail = src->nbytes - src->pos;
chunksz = dest_avail < src_avail ? dest_avail : src_avail;
memcpy(&dest->bytes[dest->pos], &src->bytes[src->pos], chunksz);
dest->pos += chunksz;
src->pos += chunksz;
return chunksz;
}
void iobuf_flip(struct const_iobuf *dest, const struct iobuf *src)
{
log_assert(dest != NULL);
log_assert(src != NULL);
dest->bytes = src->bytes;
dest->pos = 0;
dest->nbytes = src->pos;
}
void iobuf_log(struct iobuf* buffer, const char* tag)
{
char* str;
size_t str_len;
str_len = buffer->nbytes * 2 + 1;
str = xmalloc(str_len);
log_misc("[%s] (%d %d)", tag, (uint32_t) buffer->nbytes,
(uint32_t) buffer->pos);
hex_encode_uc(buffer->bytes, buffer->nbytes, str, str_len);
log_misc("[%s]: %s", tag, str);
free(str);
}
void iobuf_log_const(struct const_iobuf* buffer, const char* tag)
{
char* str;
size_t str_len;
str_len = buffer->nbytes * 2 + 1;
str = xmalloc(str_len);
log_misc("[%s] (%d %d)", tag, (uint32_t) buffer->nbytes,
(uint32_t) buffer->pos);
hex_encode_uc(buffer->bytes, buffer->nbytes, str, str_len);
log_misc("[%s]: %s", tag, str);
free(str);
}
+27
View File
@@ -0,0 +1,27 @@
#ifndef UTIL_IOBUF_H
#define UTIL_IOBUF_H
#include <stddef.h>
#include <stdint.h>
struct iobuf {
uint8_t *bytes;
size_t nbytes;
size_t pos;
};
struct const_iobuf {
const uint8_t *bytes;
size_t nbytes;
size_t pos;
};
size_t iobuf_move(struct iobuf *dest, struct const_iobuf *src);
void iobuf_flip(struct const_iobuf *dest, const struct iobuf *src);
void iobuf_log(struct iobuf* buffer, const char* tag);
void iobuf_log_const(struct const_iobuf* buffer, const char* tag);
#endif
+88
View File
@@ -0,0 +1,88 @@
#include <stddef.h>
#include <string.h>
#include "util/list.h"
extern inline struct list_node *list_peek_head(struct list *list);
extern inline const struct list_node *list_peek_head_const(
const struct list *list
);
void list_init(struct list *list)
{
memset(list, 0, sizeof(*list));
}
void list_append(struct list *list, struct list_node *node)
{
node->next = NULL;
if (list->tail != NULL) {
list->tail->next = node;
} else {
list->head = node;
}
list->tail = node;
}
bool list_contains(struct list *list, struct list_node *node)
{
struct list_node *pos;
for (pos = list->head ; pos != NULL ; pos = pos->next) {
if (pos == node) {
return true;
}
}
return false;
}
struct list_node *list_pop_head(struct list *list)
{
struct list_node *node;
if (list->head == NULL) {
return NULL;
}
node = list->head;
list->head = node->next;
if (node->next == NULL) {
list->tail = NULL;
}
node->next = NULL;
return node;
}
void list_remove(struct list *list, struct list_node *node)
{
struct list_node *pos;
struct list_node *prev;
for (prev = NULL, pos = list->head
; pos != NULL
; prev = pos, pos = pos->next) {
if (pos == node) {
if (prev != NULL) {
prev->next = node->next;
} else {
list->head = node->next;
}
if (node->next == NULL) {
list->tail = prev;
}
node->next = NULL;
return;
}
}
}
+34
View File
@@ -0,0 +1,34 @@
#ifndef UTIL_LIST_H
#define UTIL_LIST_H
#include <stdbool.h>
#include <stddef.h>
struct list {
struct list_node *head;
struct list_node *tail;
};
struct list_node {
struct list_node *next;
};
void list_init(struct list *list);
void list_append(struct list *list, struct list_node *node);
bool list_contains(struct list *list, struct list_node *node);
inline struct list_node *list_peek_head(struct list *list);
inline const struct list_node *list_peek_head_const(const struct list *list);
struct list_node *list_pop_head(struct list *list);
void list_remove(struct list *list, struct list_node *node);
inline struct list_node *list_peek_head(struct list *list)
{
return list->head;
}
inline const struct list_node *list_peek_head_const(const struct list *list)
{
return list->head;
}
#endif
+131
View File
@@ -0,0 +1,131 @@
#include "util/log.h"
#include "util/str.h"
#include <windows.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static log_writer_t log_writer;
static void *log_writer_ctx;
static unsigned int log_level;
static void log_builtin_fatal(const char *module, const char *fmt, ...);
static void log_builtin_info(const char *module, const char *fmt, ...);
static void log_builtin_misc(const char *module, const char *fmt, ...);
static void log_builtin_warning(const char *module, const char *fmt, ...);
static void log_builtin_format(unsigned int msg_level, const char *module,
const char *fmt, va_list ap);
#define IMPLEMENT_SINK(name, msg_level) \
static void name(const char *module, const char *fmt, ...) \
{ \
va_list ap; \
\
va_start(ap, fmt); \
log_builtin_format(msg_level, module, fmt, ap); \
va_end(ap); \
}
IMPLEMENT_SINK(log_builtin_info, 2)
IMPLEMENT_SINK(log_builtin_misc, 3)
IMPLEMENT_SINK(log_builtin_warning, 1)
static void log_builtin_fatal(const char *module, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
log_builtin_format(0, module, fmt, ap);
va_end(ap);
DebugBreak();
ExitProcess(EXIT_FAILURE);
}
static void log_builtin_format(unsigned int msg_level, const char *module,
const char *fmt, va_list ap)
{
static const char chars[] = "FWIM";
/* 64k so we can log data dumps of rs232 without crashing */
char line[65536];
char msg[65536];
int result;
str_vformat(msg, sizeof(msg), fmt, ap);
result = str_format(line, sizeof(line), "%c:%s: %s\n", chars[msg_level],
module, msg);
if (msg_level <= log_level) {
log_writer(log_writer_ctx, line, result);
}
}
void log_assert_body(const char *file, int line, const char *function)
{
log_impl_fatal("assert", "%s:%d: function `%s'", file, line, function);
}
void log_to_external(log_formatter_t misc, log_formatter_t info,
log_formatter_t warning, log_formatter_t fatal)
{
log_impl_misc = misc;
log_impl_info = info;
log_impl_warning = warning;
log_impl_fatal = fatal;
}
void log_to_writer(log_writer_t writer, void *ctx)
{
log_impl_misc = log_builtin_misc;
log_impl_info = log_builtin_info;
log_impl_warning = log_builtin_warning;
log_impl_fatal = log_builtin_fatal;
if (writer != NULL) {
log_writer = writer;
log_writer_ctx = ctx;
} else {
log_writer = log_writer_null;
}
}
void log_set_level(unsigned int new_level)
{
log_level = new_level;
}
void log_writer_debug(void *ctx, const char *chars, size_t nchars)
{
OutputDebugStringA(chars);
}
void log_writer_stdout(void *ctx, const char *chars, size_t nchars)
{
printf("%s", chars);
}
void log_writer_stderr(void *ctx, const char *chars, size_t nchars)
{
fprintf(stderr, "%s", chars);
}
void log_writer_file(void *ctx, const char *chars, size_t nchars)
{
fwrite(chars, 1, nchars, (FILE *) ctx);
fflush((FILE*) ctx);
}
void log_writer_null(void *ctx, const char *chars, size_t nchars)
{}
log_formatter_t log_impl_misc = log_builtin_misc;
log_formatter_t log_impl_info = log_builtin_info;
log_formatter_t log_impl_warning = log_builtin_warning;
log_formatter_t log_impl_fatal = log_builtin_fatal;
static log_writer_t log_writer = log_writer_null;
static unsigned int log_level = 4;
+75
View File
@@ -0,0 +1,75 @@
#ifndef UTIL_LOG_H
#define UTIL_LOG_H
#include <stddef.h>
#include <stdlib.h>
#include "bemanitools/glue.h"
#include "util/defs.h"
/* Dynamically retargetable logging system modeled on (and potentially
integrateable with) the one found in AVS2 */
/* BUILD_MODULE is passed in as a command-line #define by the makefile */
#ifndef LOG_MODULE
#define LOG_MODULE STRINGIFY(BUILD_MODULE)
#endif
#ifndef LOG_SUPPRESS
#define log_misc(...) log_impl_misc(LOG_MODULE, __VA_ARGS__)
#define log_info(...) log_impl_info(LOG_MODULE, __VA_ARGS__)
#define log_warning(...) log_impl_warning(LOG_MODULE, __VA_ARGS__)
/* This doesn't really belong here, but it's what libavs does so w/e */
#define log_assert(x) \
do { \
if (!(x)) { \
log_assert_body(__FILE__, __LINE__, __FUNCTION__); \
} \
} while (0)
#else
#define log_misc(...)
#define log_info(...)
#define log_warning(...)
#define log_assert(x) do { if (!(x)) { abort(); } } while (0)
#endif
#define log_fatal(...) \
do { \
log_impl_fatal(LOG_MODULE, __VA_ARGS__); \
abort(); \
} while (0)
typedef void (*log_writer_t)(void *ctx, const char *chars, size_t nchars);
extern log_formatter_t log_impl_misc;
extern log_formatter_t log_impl_info;
extern log_formatter_t log_impl_warning;
extern log_formatter_t log_impl_fatal;
void log_assert_body(const char *file, int line, const char *function);
void log_to_external(log_formatter_t misc, log_formatter_t info,
log_formatter_t warning, log_formatter_t fatal);
void log_to_writer(log_writer_t writer, void *ctx);
void log_set_level(unsigned int new_level);
/* I tried to make this API match the function signature of the AVS log writer
callback, but then the signature changed and the explicit line breaks
being passed to that callback went away. So we don't try to track that API
any more. Launcher defines its own custom writer anyway. */
void log_writer_debug(void *ctx, const char *chars, size_t nchars);
void log_writer_stdout(void *ctx, const char *chars, size_t nchars);
void log_writer_stderr(void *ctx, const char *chars, size_t nchars);
void log_writer_file(void *ctx, const char *chars, size_t nchars);
void log_writer_null(void *ctx, const char *chars, size_t nchars);
#endif
+89
View File
@@ -0,0 +1,89 @@
#define LOG_MODULE "mem"
#include <windows.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include "util/log.h"
void *xcalloc(size_t nbytes)
{
void *mem;
mem = calloc(nbytes, 1);
if (mem == NULL) {
log_fatal("xmalloc(%u) failed", (uint32_t) nbytes);
return NULL;
}
return mem;
}
void *xmalloc(size_t nbytes)
{
void *mem;
mem = malloc(nbytes);
if (mem == NULL) {
log_fatal("xcalloc(%u) failed", (uint32_t) nbytes);
return NULL;
}
return mem;
}
void *xrealloc(void *mem, size_t nbytes)
{
void *newmem;
newmem = realloc(mem, nbytes);
if (newmem == NULL) {
log_fatal("xrealloc(%p, %u) failed", mem, (uint32_t) nbytes);
return NULL;
}
return newmem;
}
bool mem_nop(size_t mem_offset, size_t length)
{
DWORD oldProt;
if (VirtualProtect((void*) (mem_offset), length, PAGE_EXECUTE_READWRITE,
&oldProt) == 0) {
return false;
}
for (size_t i = 0; i < length; i++) {
((uint8_t*) mem_offset)[i] = 0x90;
}
if (VirtualProtect((void*) (mem_offset), length, oldProt, &oldProt) == 0) {
return false;
}
return true;
}
void* mem_find_signiture(const uint8_t* sig, uint32_t sig_len,
int32_t sig_offset, void* start_addr, void* end_addr, int32_t alignment)
{
size_t pos;
for (pos = (size_t) start_addr; pos < (size_t) end_addr; pos += alignment) {
if (!memcmp((void*) pos, sig, sig_len)) {
return (void*) (pos + sig_offset);
}
}
return NULL;
}
+30
View File
@@ -0,0 +1,30 @@
#ifndef UTIL_MEM_H
#define UTIL_MEM_H
#include <stddef.h>
#include <stdbool.h>
#include <stdint.h>
void *xcalloc(size_t nbytes);
void *xmalloc(size_t nbytes);
void *xrealloc(void *mem, size_t nbytes);
bool mem_nop(size_t mem_offset, size_t length);
/**
* Scan a memory area limited by a start and end address for a signiture.
*
* @param sig Buffer with signiture to search for
* @param sig_len Length of signiture
* @param sig_offset Offset that gets added to the address where the signiture
* starts (if found)
* @param start_addr Pointer to the address to start searching at
* @param end_addr Pointer to the address to stop searching at
* @param alignment Aligns the search signiture, i.e. search in steps of
* X bytes
* @return If found, pointer to memory where the signiture was found (first
* occurance) with the signiture offset added, NULL otherwise
*/
void* mem_find_signiture(const uint8_t* sig, uint32_t sig_len,
int32_t sig_offset, void* start_addr, void* end_addr, int32_t alignment);
#endif
+118
View File
@@ -0,0 +1,118 @@
#define LOG_MODULE "msg-thread"
#include <windows.h>
#include <stdbool.h>
#include <stdint.h>
#include "util/log.h"
#include "util/msg-thread.h"
#include "util/thread.h"
static bool msg_thread_step(HWND hwnd);
static const char msg_wndclass_name[] = "msg-thread";
static int msg_thread_id;
static HANDLE msg_thread_ready;
static HANDLE msg_thread_stop;
static HWND msg_window;
static int msg_thread_proc(void *param)
{
HWND hwnd;
HINSTANCE inst;
MSG msg;
WNDCLASSEX wcx;
inst = param;
memset(&wcx, 0, sizeof(wcx));
wcx.cbSize = sizeof(wcx);
wcx.lpfnWndProc = msg_window_proc; /* callback */
wcx.hInstance = inst;
wcx.lpszClassName = msg_wndclass_name;
RegisterClassEx(&wcx);
/* Message-only windows (children of HWND_MESSAGE) do not receive
broadcast events.
Because THAT totally makes sense. */
hwnd = CreateWindowEx(0, msg_wndclass_name, NULL, 0, 0, 0, 0, 0,
NULL, NULL, inst, NULL);
msg_window_setup(hwnd); /* callback */
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
SetEvent(msg_thread_ready);
while (msg_thread_step(hwnd));
msg_window_teardown(hwnd); /* callback */
DestroyWindow(hwnd);
UnregisterClass(msg_wndclass_name, inst);
return 0;
}
static bool msg_thread_step(HWND hwnd)
{
MSG msg;
DWORD result;
result = MsgWaitForMultipleObjectsEx(1, &msg_thread_stop, INFINITE,
QS_ALLINPUT, MWMO_ALERTABLE);
switch (result) {
case WAIT_OBJECT_0:
return false;
case WAIT_OBJECT_0 + 1:
while (PeekMessage(&msg, msg_window, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return true;
case WAIT_IO_COMPLETION:
return true;
case WAIT_FAILED:
log_warning("MsgWaitForMultipleObjectsEx failed: %08x",
(unsigned int) GetLastError());
return false;
default:
log_warning("Spurious wakeup: result = %08x",
(unsigned int) result);
return true;
}
}
void msg_thread_init(HINSTANCE inst)
{
msg_thread_ready = CreateEvent(NULL, TRUE, FALSE, NULL);
msg_thread_stop = CreateEvent(NULL, TRUE, FALSE, NULL);
msg_thread_id = thread_create(msg_thread_proc, inst, 0x4000, 0);
WaitForSingleObject(msg_thread_ready, INFINITE);
CloseHandle(msg_thread_ready);
}
void msg_thread_fini(void)
{
SetEvent(msg_thread_stop);
thread_join(msg_thread_id, NULL);
thread_destroy(msg_thread_id);
CloseHandle(msg_thread_stop);
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef UTIL_MSG_THREAD_H
#define UTIL_MSG_THREAD_H
void msg_thread_init(HINSTANCE inst);
void msg_thread_fini(void);
/* Define these callbacks */
extern void msg_window_setup(HWND hwnd);
extern LRESULT WINAPI msg_window_proc(HWND hwnd, UINT msg, WPARAM wparam,
LPARAM lparam);
extern void msg_window_teardown(HWND hwnd);
#endif
+443
View File
@@ -0,0 +1,443 @@
#define LOG_MODULE "util-net"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include "util/log.h"
#include "util/mem.h"
#include "util/net.h"
#include "util/str.h"
#include <inttypes.h>
static bool net_parse_url(const char* str, struct net_addr* addr)
{
char host[NET_MAX_URL_HOSTNAME_LEN + 1];
char path[NET_MAX_URL_PATH_LEN + 1];
uint32_t ipv4[4];
uint32_t port;
int toks;
addr->type = NET_ADDR_TYPE_URL;
memset(host, 0, sizeof(host));
memset(path, 0, sizeof(path));
if (!strncmp(str, "http://", strlen("http://"))) {
addr->url.is_https = false;
str += strlen("http://");
} else if (!strncmp(str, "https://", strlen("https://"))) {
addr->url.is_https = true;
str += strlen("https://");
} else {
return false;
}
/* Try url with IPV4 address */
if (sscanf(str, "%d.%d.%d.%d", &ipv4[0], &ipv4[1], &ipv4[2],
&ipv4[3]) == 4) {
addr->url.type = NET_ADDR_TYPE_IPV4;
addr->url.ipv4.addr = (ipv4[0] & 0xFF) | ((ipv4[1] & 0xFF) << 8) |
((ipv4[2] & 0xFF) << 16) | ((ipv4[3] & 0xFF) << 24);
addr->url.ipv4.port = NET_INVALID_PORT;
memset(addr->url.path, 0, sizeof(addr->url.path));
/* Port and trailing path are optional, check with and without */
toks = sscanf(str, "%d.%d.%d.%d:%d/%s", &ipv4[0], &ipv4[1],
&ipv4[2], &ipv4[3], &port, path);
if (toks > 4) {
if (toks >= 5) {
addr->url.ipv4.port = (uint16_t) port;
}
if (toks == 6) {
strcpy(addr->url.path, path);
}
} else {
/* No port, but may have trailing path */
toks = sscanf(str, "%d.%d.%d.%d/%s", &ipv4[0], &ipv4[1],
&ipv4[2], &ipv4[3], path);
if (toks > 4) {
strcpy(addr->url.path, path);
}
}
} else {
/* URL with hostname */
addr->url.type = NET_ADDR_TYPE_HOSTNAME;
memset(addr->url.hostname.host, 0, sizeof(addr->url.hostname.host));
addr->url.hostname.port = NET_INVALID_PORT;
memset(addr->url.path, 0, sizeof(addr->url.path));
toks = sscanf(str, "%[^:]:%d/%s", host, &port, path);
if (toks == 0) {
return false;
}
if (toks >= 2) {
strcpy(addr->url.hostname.host, host);
addr->url.hostname.port = port;
if (toks == 3) {
strcpy(addr->url.path, path);
}
} else {
/* No port, but may have trailing path */
toks = sscanf(str, "%[^/]/%s", host, path);
strcpy(addr->url.hostname.host, host);
if (toks > 1) {
strcpy(addr->url.path, path);
}
}
}
return true;
}
static bool net_parse_ipv4_or_hostname(const char* str, struct net_addr* addr)
{
char host[NET_MAX_URL_HOSTNAME_LEN + 1];
uint32_t ipv4[4];
uint32_t port;
int toks;
memset(host, 0, sizeof(host));
toks = sscanf(str, "%d.%d.%d.%d:%d", &ipv4[0], &ipv4[1], &ipv4[2], &ipv4[3],
&port);
if (toks >= 4) {
addr->type = NET_ADDR_TYPE_IPV4;
addr->ipv4.addr = (ipv4[0] & 0xFF) | ((ipv4[1] & 0xFF) << 8) |
((ipv4[2] & 0xFF) << 16) | ((ipv4[3] & 0xFF) << 24);
addr->ipv4.port = NET_INVALID_PORT;
if (toks == 5) {
addr->ipv4.port = port;
}
} else if (toks == 0) {
addr->type = NET_ADDR_TYPE_HOSTNAME;
addr->hostname.port = NET_INVALID_PORT;
toks = sscanf(str, "%[^:]:%d", host, &port);
if (toks == 0) {
return false;
}
strcpy(addr->hostname.host, host);
if (toks > 1) {
addr->hostname.port = port;
}
} else {
return false;
}
return true;
}
static char* net_ipv4_to_str(const struct net_addr_ipv4* addr)
{
size_t len;
uint8_t* addr_ipv4;
char* str;
addr_ipv4 = (uint8_t*) &addr->addr;
if (addr->port != NET_INVALID_PORT) {
len = snprintf(NULL, 0, "%d.%d.%d.%d:%d", addr_ipv4[0],
addr_ipv4[1], addr_ipv4[2], addr_ipv4[3], addr->port);
str = xmalloc(len + 1);
sprintf(str, "%d.%d.%d.%d:%d", addr_ipv4[0],
addr_ipv4[1], addr_ipv4[2], addr_ipv4[3], addr->port);
} else {
len = snprintf(NULL, 0, "%d.%d.%d.%d", addr_ipv4[0], addr_ipv4[1],
addr_ipv4[2], addr_ipv4[3]);
str = xmalloc(len + 1);
sprintf(str, "%d.%d.%d.%d", addr_ipv4[0], addr_ipv4[1],
addr_ipv4[2], addr_ipv4[3]);
}
return str;
}
static char* net_hostname_to_str(const struct net_addr_hostname* addr)
{
size_t len;
char* str;
if (addr->port != NET_INVALID_PORT) {
len = snprintf(NULL, 0, "%s:%d", addr->host, addr->port);
str = xmalloc(len + 1);
sprintf(str, "%s:%d", addr->host, addr->port);
} else {
str = str_dup(addr->host);
}
return str;
}
static char* net_url_to_str(const struct net_addr_url* addr)
{
size_t len;
size_t str_len;
char* tmp;
char* str;
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
tmp = net_ipv4_to_str(&addr->ipv4);
break;
case NET_ADDR_TYPE_HOSTNAME:
tmp = net_hostname_to_str(&addr->hostname);
break;
case NET_ADDR_TYPE_INVALID:
case NET_ADDR_TYPE_URL:
/* URL is not a valid type here */
default:
log_assert(false);
return NULL;
}
str_len = strlen(addr->path);
len = snprintf(NULL, 0, "http%s://%s%s%s", addr->is_https ? "s" : "",
tmp, str_len > 0 ? "/" : "", str_len > 0 ? addr->path : "");
str = xmalloc(len + 1);
sprintf(str, "http%s://%s%s%s", addr->is_https ? "s" : "",
tmp, str_len > 0 ? "/" : "", str_len > 0 ? addr->path : "");
free(tmp);
return str;
}
static bool net_resolve_hostname_str(const char* host, uint32_t* ipv4)
{
struct WSAData data;
struct addrinfo hints;
struct addrinfo* res;
int err;
log_assert(host);
log_assert(ipv4);
/* Resolve and cache */
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
/* Using gethostbyname seems to crash on certain setups...use
getaddrinfo (which is not deprecated) */
WSAStartup(MAKEWORD(2,0), &data);
err = getaddrinfo(host, NULL, &hints, &res);
if (err != 0) {
*ipv4 = NET_INVALID_ADDR;
return false;
}
*ipv4 = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.S_un.S_addr;
freeaddrinfo(res);
WSACleanup();
return true;
}
static bool net_resolve_hostname_ipv4(const struct net_addr_ipv4* addr,
struct net_addr_ipv4* res_addr)
{
res_addr->addr = addr->addr;
res_addr->port = addr->port;
return true;
}
static bool net_resolve_hostname_host(const struct net_addr_hostname* addr,
struct net_addr_ipv4* res_addr)
{
res_addr->port = addr->port;
return net_resolve_hostname_str(addr->host, &res_addr->addr);
}
static bool net_resolve_hostname_url(const struct net_addr_url* addr,
struct net_addr_ipv4* res_addr)
{
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_resolve_hostname_ipv4(&addr->ipv4, res_addr);
case NET_ADDR_TYPE_HOSTNAME:
return net_resolve_hostname_host(&addr->hostname, res_addr);
case NET_ADDR_TYPE_INVALID:
case NET_ADDR_TYPE_URL:
/* Not a valid type here */
default:
log_assert(false);
return false;
}
}
bool net_str_parse(const char* str, struct net_addr* addr)
{
log_assert(str);
log_assert(addr);
if (!strncmp(str, "http", strlen("http"))) {
return net_parse_url(str, addr);
} else {
return net_parse_ipv4_or_hostname(str, addr);
}
}
char* net_addr_to_str(const struct net_addr* addr)
{
log_assert(addr);
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_ipv4_to_str(&addr->ipv4);
case NET_ADDR_TYPE_HOSTNAME:
return net_hostname_to_str(&addr->hostname);
case NET_ADDR_TYPE_URL:
return net_url_to_str(&addr->url);
case NET_ADDR_TYPE_INVALID:
default:
log_assert(false);
return NULL;
}
}
bool net_resolve_hostname(const char* hostname, struct net_addr_ipv4* res_addr)
{
return net_resolve_hostname_str(hostname, &res_addr->addr);
}
bool net_resolve_hostname_net_addr(const struct net_addr* addr,
struct net_addr_ipv4* res_addr)
{
switch (addr->type) {
case NET_ADDR_TYPE_IPV4:
return net_resolve_hostname_ipv4(&addr->ipv4, res_addr);
case NET_ADDR_TYPE_HOSTNAME:
return net_resolve_hostname_host(&addr->hostname, res_addr);
case NET_ADDR_TYPE_URL:
return net_resolve_hostname_url(&addr->url, res_addr);
case NET_ADDR_TYPE_INVALID:
default:
log_assert(false);
return false;
}
}
bool net_check_remote_connection(const struct net_addr* addr,
uint32_t timeout_ms)
{
struct net_addr_ipv4 target;
if (!net_resolve_hostname_net_addr(addr, &target)) {
return false;
}
return net_check_remote_connection_ipv4(&target, timeout_ms);
}
bool net_check_remote_connection_ipv4(const struct net_addr_ipv4* addr,
uint32_t timeout_ms)
{
struct WSAData data;
SOCKET s;
u_long block;
fd_set set_w;
fd_set set_e;
struct timeval timeout;
int err;
bool success;
struct sockaddr_in sockaddr;
WSAStartup(MAKEWORD(2,0), &data);
err = 0;
success = true;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET) {
log_assert(false);
return false;
}
/* Need socket in non-blocking mode to timeout */
block = 1;
if (ioctlsocket(s, FIONBIO, &block) == SOCKET_ERROR) {
log_assert(false);
closesocket(s);
return false;
}
sockaddr.sin_family = AF_INET;
sockaddr.sin_addr.s_addr = addr->addr;
sockaddr.sin_port = htons(addr->port);
if (connect(s, (struct sockaddr*) &sockaddr, sizeof(sockaddr)) ==
SOCKET_ERROR) {
if (WSAGetLastError() != WSAEWOULDBLOCK) {
/* connection failed */;
success = false;
} else {
FD_ZERO(&set_w);
FD_SET(s, &set_w);
FD_ZERO(&set_e);
FD_SET(s, &set_e);
timeout.tv_sec = timeout_ms / 1000;
timeout.tv_usec = (timeout_ms % 1000) * 1000;
err = select(0, NULL, &set_w, &set_e, &timeout);
if (err <= 0) {
/* select failed or timeout */
success = false;
} else {
if (FD_ISSET(s, &set_e)) {
/* Connection failed */
success = false;
}
}
}
} else {
success = false;
}
closesocket(s);
WSACleanup();
return success;
}
+137
View File
@@ -0,0 +1,137 @@
#ifndef UTIL_NET_H
#define UTIL_NET_H
#include <stdbool.h>
#include <stdint.h>
#define NET_INVALID_ADDR 0xFFFFFFFF
#define NET_INVALID_PORT 0xFFFF
#define NET_LOCALHOST_ADDR 0x0100007F
#define NET_LOCALHOST_NAME "localhost"
#define NET_MAX_URL_HOSTNAME_LEN 512
#define NET_MAX_URL_PATH_LEN 1024
/**
* Types of net addresses supported.
*/
enum net_addr_type {
NET_ADDR_TYPE_INVALID = 0,
NET_ADDR_TYPE_IPV4 = 1,
NET_ADDR_TYPE_HOSTNAME = 2,
NET_ADDR_TYPE_URL = 3,
};
/**
* Struct storing an IPV4 address + optional port.
*/
struct net_addr_ipv4 {
/* IP address stored in little endian byte order */
uint32_t addr;
uint16_t port;
};
/**
* Struct storing a hostname + optional port.
*/
struct net_addr_hostname {
char host[NET_MAX_URL_HOSTNAME_LEN + 1];
uint16_t port;
};
/**
* Struct storing a full https URL.
*/
struct net_addr_url {
bool is_https;
char path[NET_MAX_URL_PATH_LEN + 1];
enum net_addr_type type;
union {
struct net_addr_ipv4 ipv4;
struct net_addr_hostname hostname;
};
};
/**
* Structure representing a "network address".
*/
struct net_addr {
enum net_addr_type type;
union {
struct net_addr_ipv4 ipv4;
struct net_addr_hostname hostname;
struct net_addr_url url;
};
};
/**
* Parse a string to a net_addr sturct. The string can have one of the following
* formats:
* - Plain IPV4 address, e.g. 127.0.0.1
* - Plain IPV4 address with port, e.g. 127.0.0.1:1234
* - Plain hostname, e.g. localhost
* - Plain hostname with port, e.g. localhost:1234
* - HTTP(s) URL, e.g. http://www.myremote.com
* - HTTP(s) URL with port, e.g. http://www.myremote.com:1234
* - HTTP(s) URL with or without port and path: http://www.myremote.com:1234/somewhere
*
* @param str String to parse.
* @param addr Pointer to a net_addr struct to write the result to.
* @return True if parsing is successful, false on parse error.
*/
bool net_str_parse(const char* str, struct net_addr* addr);
// port not printed if invalid
/**
* net_addr struct to string function. Create a single c-string representation.
*
* @param addr Input net_addr.
* @return c-string representation. Note: Invalid port values are omitted.
*/
char* net_addr_to_str(const struct net_addr* addr);
/**
* Resolve a hostname to an IPV4 address.
*
* @param hostname Hostname to resolve, e.g. localhost.
* @param res_addr Pointer to a net_addr_ipv4 struct to store the result to.
* @return True if resolving was successful, false on error.
*/
bool net_resolve_hostname(const char* hostname, struct net_addr_ipv4* res_addr);
/**
* Resolve a net_addr struct to an IPV4 address. If the net_addr struct is
* actually of IPV4 type, the IPV4 address and port are simply copied to the
* result struct.
*
* @param addr net_addr struct to resolve.
* @param res_addr Pointer to a net_addr_ipv4 struct to store the result to.
* @return True if resolving was successful, false on error.
*/
bool net_resolve_hostname_net_addr(const struct net_addr* addr,
struct net_addr_ipv4* res_addr);
/**
* Check if a remote server is reachable, i.e. it is possible to open a
* connection to it.
*
* @param addr net_addr struct of target server to check.
* @param timeout_ms Timeout for the check in ms.
* @return True if target server is reachable/connectable, false otherwise.
*/
bool net_check_remote_connection(const struct net_addr* addr,
uint32_t timeout_ms);
/**
* Check if a remote server is reachable, i.e. it is possible to open a
* connection to it.
*
* @param addr net_addr_ipv4 struct of target server to check.
* @param timeout_ms Timeout for the check in ms.
* @return True if target server is reachable/connectable, false otherwise.
*/
bool net_check_remote_connection_ipv4(const struct net_addr_ipv4* addr,
uint32_t timeout_ms);
#endif
+254
View File
@@ -0,0 +1,254 @@
#include <windows.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wchar.h>
#include "util/mem.h"
#include "util/str.h"
bool str_ends_with(const char *haystack, const char *needle)
{
size_t haystack_len;
size_t needle_len;
haystack_len = strlen(haystack);
needle_len = strlen(needle);
if (needle_len > haystack_len) {
return false;
}
return !memcmp(&haystack[haystack_len - needle_len], needle, needle_len);
}
bool wstr_ends_with(const wchar_t *haystack, const wchar_t *needle)
{
size_t haystack_len;
size_t needle_len;
haystack_len = wcslen(haystack);
needle_len = wcslen(needle);
if (needle_len > haystack_len) {
return false;
}
return !memcmp(
&haystack[haystack_len - needle_len],
needle,
needle_len * sizeof(wchar_t));
}
size_t str_format(char *buf, size_t nchars, const char *fmt, ...)
{
va_list ap;
size_t result;
va_start(ap, fmt);
result = str_vformat(buf, nchars, fmt, ap);
va_end(ap);
return result;
}
size_t str_vformat(char *buf, size_t nchars, const char *fmt, va_list ap)
{
int result;
result = _vsnprintf(buf, nchars, fmt, ap);
if (result >= (int) nchars || result < 0) {
abort();
}
return (size_t) result;
}
size_t wstr_format(wchar_t *buf, size_t nchars, const wchar_t *fmt, ...)
{
va_list ap;
size_t result;
va_start(ap, fmt);
result = wstr_vformat(buf, nchars, fmt, ap);
va_end(ap);
return result;
}
size_t wstr_vformat(wchar_t *buf, size_t nchars, const wchar_t *fmt,
va_list ap)
{
int result;
result = _vsnwprintf(buf, nchars, fmt, ap);
if (result >= (int) nchars || result < 0) {
abort();
}
return (size_t) result;
}
void str_cat(char *dest, size_t dnchars, const char *src)
{
size_t dlen;
size_t slen;
dlen = strlen(dest);
slen = strlen(src);
if (dlen + slen >= dnchars) {
abort();
}
memcpy(dest + dlen, src, (slen + 1) * sizeof(char));
}
void str_cpy(char *dest, size_t dnchars, const char *src)
{
size_t slen;
slen = strlen(src);
if (slen >= dnchars) {
abort();
}
memcpy(dest, src, (slen + 1) * sizeof(char));
}
void wstr_cat(wchar_t *dest, size_t dnchars, const wchar_t *src)
{
size_t dlen;
size_t slen;
dlen = wcslen(dest);
slen = wcslen(src);
if (dlen + slen >= dnchars) {
abort();
}
memcpy(dest + dlen, src, (slen + 1) * sizeof(wchar_t));
}
void wstr_cpy(wchar_t *dest, size_t dnchars, const wchar_t *src)
{
size_t slen;
slen = wcslen(src);
if (slen >= dnchars) {
abort();
}
memcpy(dest, src, (slen + 1) * sizeof(wchar_t));
}
char *str_dup(const char *str)
{
char *dest;
size_t nbytes;
nbytes = strlen(str) + 1;
dest = xmalloc(nbytes);
memcpy(dest, str, nbytes);
return dest;
}
void str_trim(char *str)
{
char *pos;
for (pos = str + strlen(str) - 1 ; pos > str ; pos--) {
if (!isspace(*pos)) {
return;
}
*pos = '\0';
}
}
wchar_t *str_widen(const char *src)
{
int nchars;
wchar_t *result;
nchars = MultiByteToWideChar(CP_ACP, 0, src, -1, NULL, 0);
if (!nchars) {
abort();
}
result = xmalloc(nchars * sizeof(wchar_t));
if (!MultiByteToWideChar(CP_ACP, 0, src, -1, result, nchars)) {
abort();
}
return result;
}
wchar_t *wstr_dup(const wchar_t *wstr)
{
wchar_t *dest;
size_t nchars;
nchars = wcslen(wstr) + 1;
dest = xmalloc(nchars * sizeof(wchar_t));
memcpy(dest, wstr, nchars * sizeof(wchar_t));
return dest;
}
bool wstr_narrow(const wchar_t *src, char **dest)
{
int nbytes;
nbytes = WideCharToMultiByte(CP_ACP, 0, src, -1, NULL, 0, NULL, NULL);
if (nbytes <= 0) {
goto size_fail;
}
*dest = xmalloc(nbytes);
if (WideCharToMultiByte(CP_ACP, 0, src, -1, *dest, nbytes, NULL, NULL)
!= nbytes) {
goto conv_fail;
}
return true;
conv_fail:
free(*dest);
*dest = NULL;
size_fail:
return false;
}
bool str_eq(const char *lhs, const char *rhs)
{
if (lhs == NULL || rhs == NULL) {
return lhs == rhs;
} else {
return strcmp(lhs, rhs) == 0;
}
}
bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs)
{
if (lhs == NULL || rhs == NULL) {
return lhs == rhs;
} else {
return wcscmp(lhs, rhs) == 0;
}
}
+29
View File
@@ -0,0 +1,29 @@
#ifndef UTIL_STR_H
#define UTIL_STR_H
#include <stdarg.h>
#include <stddef.h>
#include <stdbool.h>
#include <wchar.h>
void str_cat(char *dest, size_t dnchars, const char *src);
void str_cpy(char *dest, size_t dnchars, const char *src);
char *str_dup(const char *src);
bool str_ends_with(const char *haystack, const char *needle);
bool str_eq(const char *lhs, const char *rhs);
size_t str_format(char *buf, size_t nchars, const char *fmt, ...);
void str_trim(char *str);
size_t str_vformat(char *buf, size_t nchars, const char *fmt, va_list ap);
wchar_t *str_widen(const char *src);
void wstr_cat(wchar_t *dest, size_t dnchars, const wchar_t *src);
void wstr_cpy(wchar_t *dest, size_t dnchars, const wchar_t *src);
wchar_t *wstr_dup(const wchar_t *src);
bool wstr_ends_with(const wchar_t *haystack, const wchar_t *needle);
bool wstr_eq(const wchar_t *lhs, const wchar_t *rhs);
size_t wstr_format(wchar_t *buf, size_t nchars, const wchar_t *fmt, ...);
bool wstr_narrow(const wchar_t *src, char **dest);
size_t wstr_vformat(wchar_t *buf, size_t nchars, const wchar_t *fmt,
va_list ap);
#endif
+93
View File
@@ -0,0 +1,93 @@
#include <windows.h>
#include <process.h>
#include <stddef.h>
#include <stdint.h>
#include "util/defs.h"
#include "util/thread.h"
struct shim_ctx {
HANDLE barrier;
int (*proc)(void *);
void *ctx;
};
thread_create_t thread_impl_create = crt_thread_create;
thread_join_t thread_impl_join = crt_thread_join;
thread_destroy_t thread_impl_destroy = crt_thread_destroy;
static unsigned int STDCALL crt_thread_shim(void *outer_ctx)
{
struct shim_ctx *sctx = outer_ctx;
int (*proc)(void *);
void *inner_ctx;
proc = sctx->proc;
inner_ctx = sctx->ctx;
SetEvent(sctx->barrier);
return proc(inner_ctx);
}
int crt_thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz,
unsigned int priority)
{
struct shim_ctx sctx;
uintptr_t thread_id;
sctx.barrier = CreateEvent(NULL, TRUE, FALSE, NULL);
sctx.proc = proc;
sctx.ctx = ctx;
thread_id = _beginthreadex(NULL, stack_sz, crt_thread_shim, &sctx, 0, NULL);
WaitForSingleObject(sctx.barrier, INFINITE);
CloseHandle(sctx.barrier);
return (int) thread_id;
}
void crt_thread_destroy(int thread_id)
{
CloseHandle((HANDLE) (uintptr_t) thread_id);
}
void crt_thread_join(int thread_id, int *result)
{
WaitForSingleObject((HANDLE) (uintptr_t) thread_id, INFINITE);
if (result) {
GetExitCodeThread((HANDLE) (uintptr_t) thread_id, (DWORD *) result);
}
}
void thread_api_init(thread_create_t create, thread_join_t join,
thread_destroy_t destroy)
{
if (create == NULL || join == NULL || destroy == NULL) {
abort();
}
thread_impl_create = create;
thread_impl_join = join;
thread_impl_destroy = destroy;
}
int thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz,
unsigned int priority)
{
return thread_impl_create(proc, ctx, stack_sz, priority);
}
void thread_join(int thread_id, int *result)
{
thread_impl_join(thread_id, result);
}
void thread_destroy(int thread_id)
{
thread_impl_destroy(thread_id);
}
+20
View File
@@ -0,0 +1,20 @@
#ifndef UTIL_THREAD_H
#define UTIL_THREAD_H
#include <stdint.h>
#include "bemanitools/glue.h"
int crt_thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz,
unsigned int priority);
void crt_thread_join(int thread_id, int *result);
void crt_thread_destroy(int thread_id);
void thread_api_init(thread_create_t create, thread_join_t join,
thread_destroy_t destroy);
int thread_create(int (*proc)(void *), void *ctx, uint32_t stack_sz,
unsigned int priority);
void thread_join(int thread_id, int *result);
void thread_destroy(int thread_id);
#endif
+54
View File
@@ -0,0 +1,54 @@
#include <windows.h>
#include "util/log.h"
#include "util/time.h"
static uint64_t counter_freq_ns;
static uint64_t counter_freq_us;
static uint64_t counter_freq_ms;
static void time_init_freq()
{
LARGE_INTEGER ticks_per_sec;
QueryPerformanceFrequency(&ticks_per_sec);
counter_freq_ms = ticks_per_sec.QuadPart / 1000;
counter_freq_us = counter_freq_ms / 1000;
counter_freq_ns = counter_freq_us / 1000;
}
uint64_t time_get_counter(void)
{
LARGE_INTEGER time;
log_assert(QueryPerformanceCounter(&time) == TRUE);
return (uint64_t) time.QuadPart;
}
uint64_t time_get_elapsed_ns(uint64_t counter_delta)
{
if (counter_freq_ns == 0) {
time_init_freq();
}
return counter_delta / counter_freq_ns;
}
uint64_t time_get_elapsed_us(uint64_t counter_delta)
{
if (counter_freq_ns == 0) {
time_init_freq();
}
return counter_delta / counter_freq_us;
}
uint32_t time_get_elapsed_ms(uint64_t counter_delta)
{
if (counter_freq_ns == 0) {
time_init_freq();
}
return counter_delta / counter_freq_ms;
}
+14
View File
@@ -0,0 +1,14 @@
#ifndef UTIL_TIME_H
#define UTIL_TIME_H
#include <stdint.h>
uint64_t time_get_counter(void);
uint64_t time_get_elapsed_ns(uint64_t counter_delta);
uint64_t time_get_elapsed_us(uint64_t counter_delta);
uint32_t time_get_elapsed_ms(uint64_t counter_delta);
#endif
+86
View File
@@ -0,0 +1,86 @@
#include <stdio.h>
#include "util/defs.h"
#include "util/str.h"
#include "util/winres.h"
void vrsprintf(char *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, va_list ap)
{
char fmt_buffer[1024];
LoadStringA(inst, fmt_resource, fmt_buffer, lengthof(fmt_buffer));
str_vformat(dest, nchars, fmt_buffer, ap);
dest[nchars - 1] = '\0';
}
void rsprintf(char *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, ...)
{
va_list ap;
va_start(ap, fmt_resource);
vrsprintf(dest, nchars, inst, fmt_resource, ap);
va_end(ap);
}
void vrswprintf(wchar_t *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, va_list ap)
{
wchar_t fmt_buffer[1024];
LoadStringW(inst, fmt_resource, fmt_buffer, lengthof(fmt_buffer));
wstr_vformat(dest, nchars, fmt_buffer, ap);
dest[nchars - 1] = L'\0';
}
void rswprintf(wchar_t *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, ...)
{
va_list ap;
va_start(ap, fmt_resource);
vrswprintf(dest, nchars, inst, fmt_resource, ap);
va_end(ap);
}
void resource_open(struct resource *res, HINSTANCE inst, const char *type,
unsigned int ord)
{
HRSRC h1;
HGLOBAL h2;
h1 = FindResourceA(inst, MAKEINTRESOURCE(ord), type);
h2 = LoadResource(inst, h1);
res->bytes = LockResource(h2);
res->nbytes = SizeofResource(inst, h1);
res->pos = 0;
}
size_t resource_fgets(struct resource *res, char *chars, size_t nchars)
{
size_t remain;
size_t i;
char c;
c = '\0';
remain = res->nbytes - res->pos;
for (i = 0 ; i < remain ; i++) {
if (c == '\n') {
break;
}
c = res->bytes[res->pos++];
if (i < nchars - 1) {
chars[i] = c;
}
}
chars[min(i, nchars - 1)] = '\0';
return i > 0;
}
+39
View File
@@ -0,0 +1,39 @@
#ifndef UTIL_WINRES_H
#define UTIL_WINRES_H
#include <windows.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <wchar.h>
#ifdef _UNICODE
#define vrstprintf vrswprintf
#define rstprintf rswprintf
#else
#define vrstprintf vrsprintf
#define rstprintf rsprintf
#endif
struct resource {
const char *bytes;
size_t nbytes;
size_t pos;
};
void vrsprintf(char *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, va_list ap);
void rsprintf(char *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, ...);
void vrswprintf(wchar_t *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, va_list ap);
void rswprintf(wchar_t *dest, size_t nchars, HINSTANCE inst,
unsigned int fmt_resource, ...);
void resource_open(struct resource *res, HINSTANCE inst, const char *type,
unsigned int ord);
size_t resource_fgets(struct resource *res, char *chars, size_t nchars);
#endif