Bemanitools v5.26 release
This commit is contained in:
@@ -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 \
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
#ifndef UTIL_CODEPAGE_H
|
||||
#define UTIL_CODEPAGE_H
|
||||
|
||||
#define CP_SHIFT_JIS 932
|
||||
|
||||
#endif
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user