mirror of
https://github.com/libretro/RetroArch
synced 2025-03-29 22:20:21 +00:00
Merge branch 'master' of https://github.com/libretro/RetroArch
This commit is contained in:
commit
417cf09967
22
CHANGES.md
Normal file
22
CHANGES.md
Normal file
@ -0,0 +1,22 @@
|
||||
# 1.4.2 (future)
|
||||
- ANDROID: Autoconf fallback
|
||||
- ANDROID: Mouse support / Emulated mouse support
|
||||
- AUTOCONF: Fix partial matches for pad name
|
||||
- CHEEVOS: Fix crashes in the cheevos description menu
|
||||
- CHEEVOS: WIP leaderboards support
|
||||
- COMMON: Threading fixes
|
||||
- DOS: Add keyboard driver
|
||||
- DOS: Improve color accuracy and scalines
|
||||
- GUI: Various settings are now only visible when advanced settings is enabled
|
||||
- LOCALIZATION: Rewrite german translation
|
||||
- LOCALIZATION: Update several english sublabels
|
||||
- NET: Allow manual netplay content loading
|
||||
- NET: Announcing network games to the public lobby is optional now
|
||||
- NET: Bake in miniupnpc
|
||||
- NET: Fix netplay join for contentless cores
|
||||
- SCANNER: Always add 7z & zip to supported extensions
|
||||
- VULKAN: Find supported composite alpha in swapchain
|
||||
- WIIU: Keyboard support
|
||||
- WINDOWS: Logging to file no longer spawns an empty window
|
||||
|
||||
# 1.4.1
|
2
Makefile
2
Makefile
@ -28,7 +28,7 @@ ifneq ($(findstring DOS,$(OS)),)
|
||||
endif
|
||||
|
||||
ifneq ($(findstring Win32,$(OS)),)
|
||||
LDFLAGS += -static-libgcc
|
||||
LDFLAGS += -static-libgcc -lwinmm
|
||||
endif
|
||||
|
||||
include Makefile.common
|
||||
|
@ -1197,7 +1197,7 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
ifeq ($(HAVE_CHEEVOS), 1)
|
||||
ifeq ($(HAVE_THREADS), 1)
|
||||
DEFINES += -DHAVE_CHEEVOS
|
||||
OBJ += cheevos.o \
|
||||
OBJ += cheevos/cheevos.o \
|
||||
$(LIBRETRO_COMM_DIR)/utils/md5.o
|
||||
endif
|
||||
endif
|
||||
@ -1208,7 +1208,26 @@ ifeq ($(HAVE_NETWORKING), 1)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_MINIUPNPC), 1)
|
||||
LIBS += -lminiupnpc
|
||||
ifeq ($(HAVE_BUILTINMINIUPNPC), 1)
|
||||
DEFINES += -DHAVE_BUILTINMINIUPNPC -DMINIUPNPC_SET_SOCKET_TIMEOUT -DMINIUPNPC_GET_SRC_ADDR
|
||||
DEFINES += -I$(DEPS_DIR)/miniupnpc
|
||||
OBJ += \
|
||||
$(DEPS_DIR)/miniupnpc/igd_desc_parse.o \
|
||||
$(DEPS_DIR)/miniupnpc/upnpreplyparse.o \
|
||||
$(DEPS_DIR)/miniupnpc/upnpcommands.o \
|
||||
$(DEPS_DIR)/miniupnpc/upnperrors.o \
|
||||
$(DEPS_DIR)/miniupnpc/connecthostport.o \
|
||||
$(DEPS_DIR)/miniupnpc/portlistingparse.o \
|
||||
$(DEPS_DIR)/miniupnpc/receivedata.o \
|
||||
$(DEPS_DIR)/miniupnpc/upnpdev.o \
|
||||
$(DEPS_DIR)/miniupnpc/minissdpc.o \
|
||||
$(DEPS_DIR)/miniupnpc/miniwget.o \
|
||||
$(DEPS_DIR)/miniupnpc/miniupnpc.o \
|
||||
$(DEPS_DIR)/miniupnpc/minixml.o \
|
||||
$(DEPS_DIR)/miniupnpc/minisoap.o
|
||||
else
|
||||
LIBS += -lminiupnpc
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -1273,7 +1292,7 @@ endif
|
||||
ifneq ($(findstring DOS,$(OS)),)
|
||||
OBJ += gfx/drivers/vga_gfx.o gfx/drivers_font/vga_font.o \
|
||||
input/drivers/dos_input.o input/drivers_joypad/dos_joypad.o \
|
||||
frontend/drivers/platform_dos.o
|
||||
frontend/drivers/platform_dos.o input/drivers_keyboard/keyboard_event_dos.o
|
||||
|
||||
ifeq ($(HAVE_MENU_COMMON), 1)
|
||||
OBJ += menu/drivers_display/menu_display_vga.o
|
||||
|
@ -23,26 +23,28 @@
|
||||
#include <libretro.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
#include "menu/menu_driver.h"
|
||||
#include "menu/menu_entries.h"
|
||||
#include "../menu/menu_driver.h"
|
||||
#include "../menu/menu_entries.h"
|
||||
#endif
|
||||
|
||||
#include "cheevos.h"
|
||||
#include "command.h"
|
||||
#include "dynamic.h"
|
||||
#include "network/net_http_special.h"
|
||||
#include "tasks/tasks_internal.h"
|
||||
#include "configuration.h"
|
||||
#include "performance_counters.h"
|
||||
#include "msg_hash.h"
|
||||
#include "runloop.h"
|
||||
#include "core.h"
|
||||
|
||||
#include "verbosity.h"
|
||||
#include "../command.h"
|
||||
#include "../dynamic.h"
|
||||
#include "../configuration.h"
|
||||
#include "../performance_counters.h"
|
||||
#include "../msg_hash.h"
|
||||
#include "../runloop.h"
|
||||
#include "../core.h"
|
||||
|
||||
#include "../network/net_http_special.h"
|
||||
#include "../tasks/tasks_internal.h"
|
||||
|
||||
#include "../verbosity.h"
|
||||
|
||||
/* Define this macro to prevent cheevos from being deactivated. */
|
||||
#undef CHEEVOS_DONT_DEACTIVATE
|
||||
@ -82,6 +84,9 @@
|
||||
#define CHEEVOS_JSON_KEY_CONSOLE_ID 0x071656e5U
|
||||
#define CHEEVOS_JSON_KEY_TOKEN 0x0e2dbd26U
|
||||
#define CHEEVOS_JSON_KEY_FLAGS 0x0d2e96b2U
|
||||
#define CHEEVOS_JSON_KEY_LEADERBOARDS 0xf1247d2dU
|
||||
#define CHEEVOS_JSON_KEY_MEM 0x0b8807e4U
|
||||
#define CHEEVOS_JSON_KEY_FORMAT 0xb341208eU
|
||||
|
||||
enum
|
||||
{
|
||||
@ -241,9 +246,11 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
int in_cheevos;
|
||||
int in_lboards;
|
||||
uint32_t field_hash;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
} cheevos_countud_t;
|
||||
|
||||
typedef struct
|
||||
@ -255,13 +262,15 @@ typedef struct
|
||||
typedef struct
|
||||
{
|
||||
int in_cheevos;
|
||||
int in_lboards;
|
||||
int is_console_id;
|
||||
unsigned core_count;
|
||||
unsigned unofficial_count;
|
||||
unsigned lboard_count;
|
||||
|
||||
cheevos_field_t *field;
|
||||
cheevos_field_t id, memaddr, title, desc, points, author;
|
||||
cheevos_field_t modified, created, badge, flags;
|
||||
cheevos_field_t modified, created, badge, flags, format;
|
||||
} cheevos_readud_t;
|
||||
|
||||
typedef struct
|
||||
@ -271,6 +280,32 @@ typedef struct
|
||||
const uint32_t *ext_hashes;
|
||||
} cheevos_finder_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cheevos_var_t var;
|
||||
int multiplier;
|
||||
} cheevos_term_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
cheevos_term_t *terms;
|
||||
unsigned count;
|
||||
} cheevos_expr_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
unsigned id;
|
||||
const char *title;
|
||||
const char *description;
|
||||
int active;
|
||||
int last_value;
|
||||
|
||||
cheevos_condition_t start;
|
||||
cheevos_condition_t cancel;
|
||||
cheevos_condition_t submit;
|
||||
cheevos_expr_t value;
|
||||
} cheevos_leaderboard_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int console_id;
|
||||
@ -278,25 +313,34 @@ typedef struct
|
||||
|
||||
cheevoset_t core;
|
||||
cheevoset_t unofficial;
|
||||
cheevos_leaderboard_t *leaderboards;
|
||||
unsigned lboard_count;
|
||||
|
||||
char token[32];
|
||||
|
||||
retro_ctx_memory_info_t meminfo[4];
|
||||
} cheevos_locals_t;
|
||||
|
||||
|
||||
static cheevos_locals_t cheevos_locals =
|
||||
{
|
||||
0,
|
||||
true,
|
||||
{NULL, 0},
|
||||
{NULL, 0},
|
||||
{0}
|
||||
/* console_id */ 0,
|
||||
/* core_supports */ true,
|
||||
/* core */ {NULL, 0},
|
||||
/* unofficial */ {NULL, 0},
|
||||
/* leaderboards */ NULL,
|
||||
/* lboard_count */ 0,
|
||||
/* token */ {0},
|
||||
{
|
||||
/* meminfo[0] */ {NULL, 0, 0},
|
||||
/* meminfo[1] */ {NULL, 0, 0},
|
||||
/* meminfo[2] */ {NULL, 0, 0},
|
||||
/* meminfo[3] */ {NULL, 0, 0}
|
||||
}
|
||||
};
|
||||
|
||||
bool cheevos_loaded = false;
|
||||
int cheats_are_enabled = 0;
|
||||
int cheats_were_enabled = 0;
|
||||
bool cheevos_loaded = false;
|
||||
int cheats_are_enabled = 0;
|
||||
int cheats_were_enabled = 0;
|
||||
|
||||
/*****************************************************************************
|
||||
Supporting functions.
|
||||
@ -405,6 +449,16 @@ static void cheevos_add_uint(char** aux, size_t* left, unsigned v)
|
||||
cheevos_add_string(aux, left, buffer);
|
||||
}
|
||||
|
||||
static void cheevos_add_int(char** aux, size_t* left, int v)
|
||||
{
|
||||
char buffer[32];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "%d", v);
|
||||
buffer[sizeof(buffer) - 1] = 0;
|
||||
|
||||
cheevos_add_string(aux, left, buffer);
|
||||
}
|
||||
|
||||
static void cheevos_log_var(const cheevos_var_t* var)
|
||||
{
|
||||
RARCH_LOG("CHEEVOS size: %s\n",
|
||||
@ -608,6 +662,43 @@ static void cheevos_post_log_cheevo(const cheevo_t* cheevo)
|
||||
cheevos_build_memaddr(&cheevo->condition, memaddr, sizeof(memaddr));
|
||||
RARCH_LOG("CHEEVOS memaddr (computed): %s\n", memaddr);
|
||||
}
|
||||
|
||||
static void cheevos_log_lboard(const cheevos_leaderboard_t* lb)
|
||||
{
|
||||
char mem[256];
|
||||
char* aux;
|
||||
size_t left;
|
||||
unsigned i;
|
||||
|
||||
RARCH_LOG("CHEEVOS leaderboard %p\n", lb);
|
||||
RARCH_LOG("CHEEVOS id: %u\n", lb->id);
|
||||
RARCH_LOG("CHEEVOS title: %s\n", lb->title);
|
||||
RARCH_LOG("CHEEVOS desc: %s\n", lb->description);
|
||||
|
||||
cheevos_build_memaddr(&lb->start, mem, sizeof(mem));
|
||||
RARCH_LOG("CHEEVOS start: %s\n", mem);
|
||||
|
||||
cheevos_build_memaddr(&lb->cancel, mem, sizeof(mem));
|
||||
RARCH_LOG("CHEEVOS cancel: %s\n", mem);
|
||||
|
||||
cheevos_build_memaddr(&lb->submit, mem, sizeof(mem));
|
||||
RARCH_LOG("CHEEVOS submit: %s\n", mem);
|
||||
|
||||
left = sizeof(mem);
|
||||
aux = mem;
|
||||
|
||||
for (i = 0; i < lb->value.count; i++)
|
||||
{
|
||||
if (i != 0)
|
||||
cheevos_add_char(&aux, &left, '_');
|
||||
|
||||
cheevos_add_var(&lb->value.terms[i].var, &aux, &left);
|
||||
cheevos_add_char(&aux, &left, '*');
|
||||
cheevos_add_int(&aux, &left, lb->value.terms[i].multiplier);
|
||||
}
|
||||
|
||||
RARCH_LOG("CHEEVOS value: %s\n", mem);
|
||||
}
|
||||
#endif
|
||||
|
||||
static uint32_t cheevos_djb2(const char* str, size_t length)
|
||||
@ -633,11 +724,19 @@ static int cheevos_http_get(const char **result, size_t *size,
|
||||
int ret = net_http_get(result, size, url, timeout);
|
||||
#endif
|
||||
|
||||
if (ret == NET_HTTP_GET_OK)
|
||||
{
|
||||
if (*result != NULL)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
RARCH_ERR("CHEEVOS error during HTTP GET: ok.\n");
|
||||
return NET_HTTP_GET_CONNECT_ERROR;
|
||||
}
|
||||
|
||||
switch (ret)
|
||||
{
|
||||
case NET_HTTP_GET_OK:
|
||||
return ret;
|
||||
|
||||
case NET_HTTP_GET_MALFORMED_URL:
|
||||
msg = "malformed url";
|
||||
break;
|
||||
@ -651,11 +750,11 @@ static int cheevos_http_get(const char **result, size_t *size,
|
||||
break;
|
||||
|
||||
default:
|
||||
msg = "?";
|
||||
msg = "unknown error";
|
||||
break;
|
||||
}
|
||||
|
||||
RARCH_ERR("CHEEVOS error getting %s: %s.\n", url, msg);
|
||||
RARCH_ERR("CHEEVOS error during HTTP GET: %s.\n", msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -756,6 +855,7 @@ static int cheevos_count__json_end_array(void *userdata)
|
||||
{
|
||||
cheevos_countud_t* ud = (cheevos_countud_t*)userdata;
|
||||
ud->in_cheevos = 0;
|
||||
ud->in_lboards = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -767,6 +867,8 @@ static int cheevos_count__json_key(void *userdata,
|
||||
|
||||
if (ud->field_hash == CHEEVOS_JSON_KEY_ACHIEVEMENTS)
|
||||
ud->in_cheevos = 1;
|
||||
else if (ud->field_hash == CHEEVOS_JSON_KEY_LEADERBOARDS)
|
||||
ud->in_lboards = 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -785,12 +887,15 @@ static int cheevos_count__json_number(void *userdata,
|
||||
else if (flags == 5)
|
||||
ud->unofficial_count++; /* Unofficial achievements */
|
||||
}
|
||||
else if (ud->in_lboards && ud->field_hash == CHEEVOS_JSON_KEY_ID)
|
||||
ud->lboard_count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cheevos_count_cheevos(const char *json,
|
||||
unsigned *core_count, unsigned *unofficial_count)
|
||||
unsigned *core_count, unsigned *unofficial_count,
|
||||
unsigned *lboard_count)
|
||||
{
|
||||
static const jsonsax_handlers_t handlers =
|
||||
{
|
||||
@ -813,11 +918,13 @@ static int cheevos_count_cheevos(const char *json,
|
||||
ud.in_cheevos = 0;
|
||||
ud.core_count = 0;
|
||||
ud.unofficial_count = 0;
|
||||
ud.lboard_count = 0;
|
||||
|
||||
res = jsonsax_parse(json, &handlers, (void*)&ud);
|
||||
|
||||
*core_count = ud.core_count;
|
||||
*unofficial_count = ud.unofficial_count;
|
||||
*lboard_count = ud.lboard_count;
|
||||
|
||||
return res;
|
||||
}
|
||||
@ -1061,7 +1168,12 @@ static void cheevos_parse_var(cheevos_var_t *var, const char **memaddr)
|
||||
if (toupper(*str) == 'H')
|
||||
str++;
|
||||
else
|
||||
{
|
||||
if (toupper(*str) == 'V')
|
||||
str++;
|
||||
|
||||
base = 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (var->type != CHEEVOS_VAR_TYPE_VALUE_COMP)
|
||||
@ -1256,6 +1368,117 @@ static int cheevos_parse_condition(cheevos_condition_t *condition, const char* m
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static void cheevos_free_condition(cheevos_condition_t* condition)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
if (condition->condsets)
|
||||
{
|
||||
for (i = 0; i < condition->count; i++)
|
||||
{
|
||||
free((void*)condition->condsets[i].conds);
|
||||
}
|
||||
|
||||
free((void*)condition->condsets);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
Parse the Mem field of leaderboards.
|
||||
*****************************************************************************/
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static int cheevos_parse_expression(cheevos_expr_t *expr, const char* mem)
|
||||
{
|
||||
const char* aux;
|
||||
char* end;
|
||||
unsigned i;
|
||||
expr->count = 1;
|
||||
|
||||
for (aux = mem; *aux != '"'; aux++)
|
||||
{
|
||||
expr->count += *aux == '_';
|
||||
}
|
||||
|
||||
expr->terms = (cheevos_term_t*)calloc(expr->count, sizeof(cheevos_term_t));
|
||||
|
||||
if (!expr->terms)
|
||||
return -1;
|
||||
|
||||
for (i = 0, aux = mem; i < expr->count; i++)
|
||||
{
|
||||
cheevos_parse_var(&expr->terms[i].var, &aux);
|
||||
|
||||
if (*aux != '*')
|
||||
{
|
||||
free((void*)expr->terms);
|
||||
return -1;
|
||||
}
|
||||
|
||||
expr->terms[i].multiplier = (int)strtol(aux + 1, &end, 10);
|
||||
aux = end + 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static int cheevos_parse_mem(cheevos_leaderboard_t *lb, const char* mem)
|
||||
{
|
||||
lb->start.condsets = NULL;
|
||||
lb->cancel.condsets = NULL;
|
||||
lb->submit.condsets = NULL;
|
||||
lb->value.terms = NULL;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if (*mem == 'S' && mem[1] == 'T' && mem[2] == 'A' && mem[3] == ':')
|
||||
{
|
||||
if (cheevos_parse_condition(&lb->start, mem + 4))
|
||||
goto error;
|
||||
}
|
||||
else if (*mem == 'C' && mem[1] == 'A' && mem[2] == 'N' && mem[3] == ':')
|
||||
{
|
||||
if (cheevos_parse_condition(&lb->cancel, mem + 4))
|
||||
goto error;
|
||||
}
|
||||
else if (*mem == 'S' && mem[1] == 'U' && mem[2] == 'B' && mem[3] == ':')
|
||||
{
|
||||
if (cheevos_parse_condition(&lb->submit, mem + 4))
|
||||
goto error;
|
||||
}
|
||||
else if (*mem == 'V' && mem[1] == 'A' && mem[2] == 'L' && mem[3] == ':')
|
||||
{
|
||||
if (cheevos_parse_expression(&lb->value, mem + 4))
|
||||
goto error;
|
||||
}
|
||||
else
|
||||
goto error;
|
||||
|
||||
for (mem += 4;; mem++)
|
||||
{
|
||||
if (*mem == ':' && mem[1] == ':')
|
||||
{
|
||||
mem += 2;
|
||||
break;
|
||||
}
|
||||
else if (*mem == '"')
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
error:
|
||||
cheevos_free_condition(&lb->start);
|
||||
cheevos_free_condition(&lb->cancel);
|
||||
cheevos_free_condition(&lb->submit);
|
||||
free((void*)lb->value.terms);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
Load achievements from a JSON string.
|
||||
*****************************************************************************/
|
||||
@ -1317,11 +1540,40 @@ error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static int cheevos_new_lboard(cheevos_readud_t *ud)
|
||||
{
|
||||
cheevos_leaderboard_t *lboard = cheevos_locals.leaderboards + ud->lboard_count++;
|
||||
|
||||
lboard->id = strtol(ud->id.string, NULL, 10);
|
||||
lboard->title = cheevos_dupstr(&ud->title);
|
||||
lboard->description = cheevos_dupstr(&ud->desc);
|
||||
|
||||
if (!lboard->title || !lboard->description)
|
||||
goto error;
|
||||
|
||||
if (cheevos_parse_mem(lboard, ud->memaddr.string))
|
||||
goto error;
|
||||
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
cheevos_log_lboard(lboard);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free((void*)lboard->title);
|
||||
free((void*)lboard->description);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cheevos_read__json_key( void *userdata,
|
||||
const char *name, size_t length)
|
||||
{
|
||||
cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
|
||||
uint32_t hash = cheevos_djb2(name, length);
|
||||
int common = ud->in_cheevos || ud->in_lboards;
|
||||
|
||||
ud->field = NULL;
|
||||
|
||||
@ -1330,23 +1582,30 @@ static int cheevos_read__json_key( void *userdata,
|
||||
case CHEEVOS_JSON_KEY_ACHIEVEMENTS:
|
||||
ud->in_cheevos = 1;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_LEADERBOARDS:
|
||||
ud->in_lboards = 1;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_CONSOLE_ID:
|
||||
ud->is_console_id = 1;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_ID:
|
||||
if (ud->in_cheevos)
|
||||
if (common)
|
||||
ud->field = &ud->id;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_MEMADDR:
|
||||
if (ud->in_cheevos)
|
||||
ud->field = &ud->memaddr;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_MEM:
|
||||
if (ud->in_lboards)
|
||||
ud->field = &ud->memaddr;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_TITLE:
|
||||
if (ud->in_cheevos)
|
||||
if (common)
|
||||
ud->field = &ud->title;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_DESCRIPTION:
|
||||
if (ud->in_cheevos)
|
||||
if (common)
|
||||
ud->field = &ud->desc;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_POINTS:
|
||||
@ -1373,6 +1632,10 @@ static int cheevos_read__json_key( void *userdata,
|
||||
if (ud->in_cheevos)
|
||||
ud->field = &ud->flags;
|
||||
break;
|
||||
case CHEEVOS_JSON_KEY_FORMAT:
|
||||
if (ud->in_lboards)
|
||||
ud->field = &ud->format;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1419,6 +1682,10 @@ static int cheevos_read__json_end_object(void *userdata)
|
||||
|
||||
if (ud->in_cheevos)
|
||||
return cheevos_new_cheevo(ud);
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
else if (ud->in_lboards)
|
||||
return cheevos_new_lboard(ud);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -1427,6 +1694,7 @@ static int cheevos_read__json_end_array(void *userdata)
|
||||
{
|
||||
cheevos_readud_t *ud = (cheevos_readud_t*)userdata;
|
||||
ud->in_cheevos = 0;
|
||||
ud->in_lboards = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1448,7 +1716,7 @@ static int cheevos_parse(const char *json)
|
||||
NULL
|
||||
};
|
||||
|
||||
unsigned core_count, unofficial_count;
|
||||
unsigned core_count, unofficial_count, lboard_count;
|
||||
int res;
|
||||
cheevos_readud_t ud;
|
||||
settings_t *settings = config_get_ptr();
|
||||
@ -1458,7 +1726,8 @@ static int cheevos_parse(const char *json)
|
||||
return 0;
|
||||
|
||||
/* Count the number of achievements in the JSON file. */
|
||||
res = cheevos_count_cheevos(json, &core_count, &unofficial_count);
|
||||
res = cheevos_count_cheevos(json, &core_count, &unofficial_count,
|
||||
&lboard_count);
|
||||
|
||||
if (res != JSONSAX_OK)
|
||||
return -1;
|
||||
@ -1473,21 +1742,30 @@ static int cheevos_parse(const char *json)
|
||||
calloc(unofficial_count, sizeof(cheevo_t));
|
||||
cheevos_locals.unofficial.count = unofficial_count;
|
||||
|
||||
if (!cheevos_locals.core.cheevos || !cheevos_locals.unofficial.cheevos)
|
||||
cheevos_locals.leaderboards = (cheevos_leaderboard_t*)
|
||||
calloc(lboard_count, sizeof(cheevos_leaderboard_t));
|
||||
cheevos_locals.lboard_count = lboard_count;
|
||||
|
||||
if ( !cheevos_locals.core.cheevos || !cheevos_locals.unofficial.cheevos
|
||||
|| !cheevos_locals.leaderboards)
|
||||
{
|
||||
free((void*)cheevos_locals.core.cheevos);
|
||||
free((void*)cheevos_locals.unofficial.cheevos);
|
||||
cheevos_locals.core.count = cheevos_locals.unofficial.count = 0;
|
||||
free((void*)cheevos_locals.leaderboards);
|
||||
cheevos_locals.core.count = cheevos_locals.unofficial.count =
|
||||
cheevos_locals.lboard_count = 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Load the achievements. */
|
||||
ud.in_cheevos = 0;
|
||||
ud.in_lboards = 0;
|
||||
ud.is_console_id = 0;
|
||||
ud.field = NULL;
|
||||
ud.core_count = 0;
|
||||
ud.unofficial_count = 0;
|
||||
ud.lboard_count = 0;
|
||||
|
||||
if (jsonsax_parse(json, &handlers, (void*)&ud) != JSONSAX_OK)
|
||||
goto error;
|
||||
@ -1535,7 +1813,6 @@ static unsigned cheevos_get_var_value(cheevos_var_t *var)
|
||||
if ( var->type == CHEEVOS_VAR_TYPE_ADDRESS
|
||||
|| var->type == CHEEVOS_VAR_TYPE_DELTA_MEM)
|
||||
{
|
||||
/* TODO Check with Scott if the bank id is needed */
|
||||
const uint8_t *memory = cheevos_get_memory(var);
|
||||
unsigned live_val = 0;
|
||||
|
||||
@ -1950,6 +2227,162 @@ static void cheevos_test_cheevo_set(const cheevoset_t *set)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static int cheevos_test_lboard_condition(const cheevos_condition_t* condition)
|
||||
{
|
||||
int dirty_conds = 0;
|
||||
int reset_conds = 0;
|
||||
int ret_val = 0;
|
||||
int ret_val_sub_cond = condition->count == 1;
|
||||
cheevos_condset_t *condset = condition->condsets;
|
||||
const cheevos_condset_t *end = condset + condition->count;
|
||||
|
||||
if (condset < end)
|
||||
{
|
||||
ret_val = cheevos_test_cond_set(condset, &dirty_conds, &reset_conds, 0);
|
||||
condset++;
|
||||
}
|
||||
|
||||
while (condset < end)
|
||||
{
|
||||
ret_val_sub_cond |= cheevos_test_cond_set(condset, &dirty_conds, &reset_conds, 0);
|
||||
condset++;
|
||||
}
|
||||
|
||||
if (reset_conds)
|
||||
{
|
||||
for (condset = condition->condsets; condset < end; condset++)
|
||||
cheevos_reset_cond_set(condset, 0);
|
||||
}
|
||||
|
||||
return ret_val && ret_val_sub_cond;
|
||||
}
|
||||
|
||||
static int cheevos_expr_value(cheevos_expr_t* expr)
|
||||
{
|
||||
cheevos_term_t* term = expr->terms;
|
||||
unsigned i;
|
||||
int value = 0;
|
||||
|
||||
for (i = expr->count; i != 0; i--, term++)
|
||||
{
|
||||
value += cheevos_get_var_value(&term->var) * term->multiplier;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void cheevos_make_lboard_url(const cheevos_leaderboard_t *lboard,
|
||||
char* url, size_t url_size)
|
||||
{
|
||||
settings_t *settings = config_get_ptr();
|
||||
char signature[64];
|
||||
MD5_CTX ctx;
|
||||
uint8_t hash[16];
|
||||
|
||||
hash[0] = '\0';
|
||||
|
||||
snprintf(signature, sizeof(signature), "%u%s%u", lboard->id,
|
||||
settings->cheevos.username, lboard->id);
|
||||
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, (void*)signature, strlen(signature));
|
||||
MD5_Final(hash, &ctx);
|
||||
|
||||
snprintf(
|
||||
url, url_size,
|
||||
"http://retroachievements.org/dorequest.php?r=submitlbentry&u=%s&t=%s&i=%u&s=%d"
|
||||
"&v=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
||||
settings->cheevos.username, cheevos_locals.token, lboard->id,
|
||||
lboard->last_value,
|
||||
hash[ 0], hash[ 1], hash[ 2], hash[ 3],
|
||||
hash[ 4], hash[ 5], hash[ 6], hash[ 7],
|
||||
hash[ 8], hash[ 9], hash[10], hash[11],
|
||||
hash[12], hash[13], hash[14], hash[15]
|
||||
);
|
||||
|
||||
url[url_size - 1] = 0;
|
||||
|
||||
#ifdef CHEEVOS_LOG_URLS
|
||||
cheevos_log_url("CHEEVOS url to submit the leaderboard: %s\n", url);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static void cheevos_lboard_submit(void *task_data, void *user_data, const char *error)
|
||||
{
|
||||
cheevos_leaderboard_t *lboard = (cheevos_leaderboard_t *)user_data;
|
||||
|
||||
if (error == NULL)
|
||||
{
|
||||
RARCH_LOG("CHEEVOS submitted leaderboard %u.\n", lboard->id);
|
||||
}
|
||||
else
|
||||
RARCH_ERR("CHEEVOS error submitting leaderboard %u\n", lboard->id);
|
||||
#if 0
|
||||
{
|
||||
char url[256];
|
||||
url[0] = '\0';
|
||||
|
||||
RARCH_ERR("CHEEVOS error submitting leaderboard %u, retrying...\n", lboard->id);
|
||||
|
||||
cheevos_make_lboard_url(lboard, url, sizeof(url));
|
||||
task_push_http_transfer(url, true, NULL, cheevos_lboard_submit, lboard);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
static void cheevos_test_leaderboards(void)
|
||||
{
|
||||
cheevos_leaderboard_t* lboard = cheevos_locals.leaderboards;
|
||||
unsigned i;
|
||||
|
||||
for (i = cheevos_locals.lboard_count; i != 0; i--, lboard++)
|
||||
{
|
||||
if (lboard->active)
|
||||
{
|
||||
int value = cheevos_expr_value(&lboard->value);
|
||||
|
||||
if (value != lboard->last_value)
|
||||
{
|
||||
#ifdef CHEEVOS_VERBOSE
|
||||
RARCH_LOG("CHEEVOS value lboard %s %u\n", lboard->title, value);
|
||||
#endif
|
||||
lboard->last_value = value;
|
||||
}
|
||||
|
||||
if (cheevos_test_lboard_condition(&lboard->submit))
|
||||
{
|
||||
char url[256];
|
||||
|
||||
cheevos_make_lboard_url(lboard, url, sizeof(url));
|
||||
task_push_http_transfer(url, true, NULL, cheevos_lboard_submit, lboard);
|
||||
|
||||
RARCH_LOG("CHEEVOS submit lboard %s\n", lboard->title);
|
||||
}
|
||||
|
||||
if (cheevos_test_lboard_condition(&lboard->cancel))
|
||||
{
|
||||
RARCH_LOG("CHEEVOS cancel lboard %s\n", lboard->title);
|
||||
lboard->active = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cheevos_test_lboard_condition(&lboard->start))
|
||||
{
|
||||
RARCH_LOG("CHEEVOS start lboard %s\n", lboard->title);
|
||||
lboard->active = 1;
|
||||
lboard->last_value = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/*****************************************************************************
|
||||
Free the loaded achievements.
|
||||
*****************************************************************************/
|
||||
@ -2698,13 +3131,17 @@ void cheevos_reset_game(void)
|
||||
const cheevo_t *end = cheevo + cheevos_locals.core.count;
|
||||
|
||||
for (; cheevo < end; cheevo++)
|
||||
{
|
||||
cheevo->last = 1;
|
||||
}
|
||||
|
||||
cheevo = cheevos_locals.unofficial.cheevos;
|
||||
end = cheevo + cheevos_locals.unofficial.count;
|
||||
|
||||
for (; cheevo < end; cheevo++)
|
||||
{
|
||||
cheevo->last = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void cheevos_populate_menu(void *data, bool hardcore)
|
||||
@ -2814,15 +3251,23 @@ void cheevos_populate_menu(void *data, bool hardcore)
|
||||
|
||||
bool cheevos_get_description(cheevos_ctx_desc_t *desc)
|
||||
{
|
||||
cheevo_t *cheevos = cheevos_locals.core.cheevos;
|
||||
|
||||
if (desc->idx >= cheevos_locals.core.count)
|
||||
if (cheevos_loaded)
|
||||
{
|
||||
cheevos = cheevos_locals.unofficial.cheevos;
|
||||
desc->idx -= cheevos_locals.unofficial.count;
|
||||
}
|
||||
cheevo_t *cheevos = cheevos_locals.core.cheevos;
|
||||
|
||||
strlcpy(desc->s, cheevos[desc->idx].description, desc->len);
|
||||
if (desc->idx >= cheevos_locals.core.count)
|
||||
{
|
||||
cheevos = cheevos_locals.unofficial.cheevos;
|
||||
desc->idx -= cheevos_locals.unofficial.count;
|
||||
}
|
||||
|
||||
strlcpy(desc->s, cheevos[desc->idx].description, desc->len);
|
||||
}
|
||||
else
|
||||
{
|
||||
*desc->s = 0;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -2842,7 +3287,7 @@ bool cheevos_unload(void)
|
||||
cheevos_free_cheevo_set(&cheevos_locals.core);
|
||||
cheevos_free_cheevo_set(&cheevos_locals.unofficial);
|
||||
|
||||
cheevos_loaded = false;
|
||||
cheevos_loaded = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -2881,7 +3326,7 @@ void cheevos_test(void)
|
||||
if (settings->cheevos.test_unofficial)
|
||||
cheevos_test_cheevo_set(&cheevos_locals.unofficial);
|
||||
|
||||
#if 0
|
||||
#ifdef CHEEVOS_ENABLE_LBOARDS
|
||||
cheevos_test_leaderboards();
|
||||
#endif
|
||||
}
|
14
command.c
14
command.c
@ -43,7 +43,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "cheevos.h"
|
||||
#include "cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
@ -1350,15 +1350,6 @@ static bool event_init_content(void)
|
||||
|
||||
content_get_status(&contentless, &is_inited);
|
||||
|
||||
if (contentless)
|
||||
{
|
||||
#ifdef HAVE_NETWORKING
|
||||
if (netplay_driver_ctl(RARCH_NETPLAY_CTL_IS_ENABLED, NULL))
|
||||
RARCH_ERR("%s\n", msg_hash_to_str(MSG_SORRY_UNIMPLEMENTED_CORES_DONT_DEMAND_CONTENT_NETPLAY));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
command_event_set_savestate_auto_index();
|
||||
|
||||
if (event_load_save_files())
|
||||
@ -1927,6 +1918,9 @@ bool command_event(enum event_command cmd, void *data)
|
||||
core_reset();
|
||||
#ifdef HAVE_CHEEVOS
|
||||
cheevos_reset_game();
|
||||
#endif
|
||||
#if HAVE_NETWORKING
|
||||
netplay_driver_ctl(RARCH_NETPLAY_CTL_RESET, NULL);
|
||||
#endif
|
||||
break;
|
||||
case CMD_EVENT_SAVE_STATE:
|
||||
|
@ -815,6 +815,9 @@ static const bool pause_nonactive = true;
|
||||
* It is measured in seconds. A value of 0 disables autosave. */
|
||||
static const unsigned autosave_interval = 0;
|
||||
|
||||
/* Publicly announce netplay */
|
||||
static const bool netplay_public_announce = true;
|
||||
|
||||
/* Netplay without savestates/rewind */
|
||||
static const bool netplay_stateless_mode = false;
|
||||
|
||||
|
@ -728,6 +728,7 @@ static struct config_bool_setting *populate_settings_bool(settings_t *settings,
|
||||
SETTING_BOOL("all_users_control_menu", &settings->input.all_users_control_menu, true, all_users_control_menu, false);
|
||||
SETTING_BOOL("menu_swap_ok_cancel_buttons", &settings->input.menu_swap_ok_cancel_buttons, true, menu_swap_ok_cancel_buttons, false);
|
||||
#ifdef HAVE_NETWORKING
|
||||
SETTING_BOOL("netplay_public_announce", &settings->netplay.public_announce, true, netplay_public_announce, false);
|
||||
SETTING_BOOL("netplay_stateless_mode", &settings->netplay.stateless_mode, false, netplay_stateless_mode, false);
|
||||
SETTING_BOOL("netplay_client_swap_input", &settings->netplay.swap_input, true, netplay_client_swap_input, false);
|
||||
#endif
|
||||
@ -1671,7 +1672,7 @@ static bool check_shader_compatibility(enum file_path_enum enum_idx)
|
||||
}
|
||||
|
||||
if (string_is_equal("gl", settings->video.driver) ||
|
||||
string_is_equal("d3d9", settings->video.driver))
|
||||
string_is_equal("d3d", settings->video.driver))
|
||||
{
|
||||
if (enum_idx == FILE_PATH_SLANGP_EXTENSION)
|
||||
return false;
|
||||
|
@ -403,6 +403,7 @@ typedef struct settings
|
||||
#ifdef HAVE_NETWORKING
|
||||
struct
|
||||
{
|
||||
bool public_announce;
|
||||
char server[255];
|
||||
unsigned port;
|
||||
bool stateless_mode;
|
||||
|
@ -59,6 +59,8 @@ static void core_info_list_resolve_all_extensions(
|
||||
(strlen(core_info_list->list[i].supported_extensions) + 2);
|
||||
}
|
||||
|
||||
all_ext_len += strlen("7z|") + strlen("zip|");
|
||||
|
||||
if (all_ext_len)
|
||||
all_ext = (char*)calloc(1, all_ext_len);
|
||||
|
||||
@ -76,6 +78,12 @@ static void core_info_list_resolve_all_extensions(
|
||||
core_info_list->list[i].supported_extensions, all_ext_len);
|
||||
strlcat(core_info_list->all_ext, "|", all_ext_len);
|
||||
}
|
||||
#ifdef HAVE_7ZIP
|
||||
strlcat(core_info_list->all_ext, "7z|", all_ext_len);
|
||||
#endif
|
||||
#ifdef HAVE_ZLIB
|
||||
strlcat(core_info_list->all_ext, "zip|", all_ext_len);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void core_info_list_resolve_all_firmware(
|
||||
|
684
deps/miniupnpc/Changelog.txt
vendored
Normal file
684
deps/miniupnpc/Changelog.txt
vendored
Normal file
@ -0,0 +1,684 @@
|
||||
$Id: Changelog.txt,v 1.226 2016/12/16 08:57:19 nanard Exp $
|
||||
miniUPnP client Changelog.
|
||||
|
||||
2016/11/11:
|
||||
check strlen before memcmp in XML parsing portlistingparse.c
|
||||
fix build under SOLARIS and CYGWIN
|
||||
|
||||
2016/10/11:
|
||||
Add python 3 compatibility to IGD test
|
||||
|
||||
VERSION 2.0 : released 2016/04/19
|
||||
|
||||
2016/01/24:
|
||||
change miniwget to return HTTP status code
|
||||
increments API_VERSION to 16
|
||||
|
||||
2016/01/22:
|
||||
Improve UPNPIGD_IsConnected() to check if WAN address is not private.
|
||||
parse HTTP response status line in miniwget.c
|
||||
|
||||
2015/10/26:
|
||||
snprintf() overflow check. check overflow in simpleUPnPcommand2()
|
||||
|
||||
2015/10/25:
|
||||
fix compilation with old macs
|
||||
fix compilation with mingw32 (for Appveyor)
|
||||
fix python module for python <= 2.3
|
||||
|
||||
2015/10/08:
|
||||
Change sameport to localport
|
||||
see https://github.com/miniupnp/miniupnp/pull/120
|
||||
increments API_VERSION to 15
|
||||
|
||||
2015/09/15:
|
||||
Fix buffer overflow in igd_desc_parse.c/IGDstartelt()
|
||||
Discovered by Aleksandar Nikolic of Cisco Talos
|
||||
|
||||
2015/08/28:
|
||||
move ssdpDiscoverDevices() to minissdpc.c
|
||||
|
||||
2015/08/27:
|
||||
avoid unix socket leak in getDevicesFromMiniSSDPD()
|
||||
|
||||
2015/08/16:
|
||||
Also accept "Up" as ConnectionStatus value
|
||||
|
||||
2015/07/23:
|
||||
split getDevicesFromMiniSSDPD
|
||||
add ttl argument to upnpDiscover() functions
|
||||
increments API_VERSION to 14
|
||||
|
||||
2015/07/22:
|
||||
Read USN from SSDP messages.
|
||||
|
||||
2015/07/15:
|
||||
Check malloc/calloc
|
||||
|
||||
2015/06/16:
|
||||
update getDevicesFromMiniSSDPD() to process longer minissdpd
|
||||
responses
|
||||
|
||||
2015/05/22:
|
||||
add searchalltypes param to upnpDiscoverDevices()
|
||||
increments API_VERSION to 13
|
||||
|
||||
2015/04/30:
|
||||
upnpc: output version on the terminal
|
||||
|
||||
2015/04/27:
|
||||
_BSD_SOURCE is deprecated in favor of _DEFAULT_SOURCE
|
||||
fix CMakeLists.txt COMPILE_DEFINITIONS
|
||||
fix getDevicesFromMiniSSDPD() not setting scope_id
|
||||
improve -r command of upnpc command line tool
|
||||
|
||||
2014/11/17:
|
||||
search all :
|
||||
upnpDiscoverDevices() / upnpDiscoverAll() functions
|
||||
listdevices executable
|
||||
increment API_VERSION to 12
|
||||
validate igd_desc_parse
|
||||
|
||||
2014/11/13:
|
||||
increment API_VERSION to 11
|
||||
|
||||
2014/11/05:
|
||||
simplified function GetUPNPUrls()
|
||||
|
||||
2014/09/11:
|
||||
use remoteHost arg of DeletePortMapping
|
||||
|
||||
2014/09/06:
|
||||
Fix python3 build
|
||||
|
||||
2014/07/01:
|
||||
Fix parsing of IGD2 root descriptions
|
||||
|
||||
2014/06/10:
|
||||
rename LIBSPEC to MINIUPNP_LIBSPEC
|
||||
|
||||
2014/05/15:
|
||||
Add support for IGD2 AddAnyPortMapping and DeletePortMappingRange
|
||||
|
||||
2014/02/05:
|
||||
handle EINPROGRESS after connect()
|
||||
|
||||
2014/02/03:
|
||||
minixml now handle XML comments
|
||||
|
||||
VERSION 1.9 : released 2014/01/31
|
||||
|
||||
2014/01/31:
|
||||
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
|
||||
increment API_VERSION to 10
|
||||
|
||||
2013/12/09:
|
||||
--help and -h arguments in upnpc.c
|
||||
|
||||
2013/10/07:
|
||||
fixed potential buffer overrun in miniwget.c
|
||||
Modified UPNP_GetValidIGD() to check for ExternalIpAddress
|
||||
|
||||
2013/08/01:
|
||||
define MAXHOSTNAMELEN if not already done
|
||||
|
||||
2013/06/06:
|
||||
update upnpreplyparse to allow larger values (128 chars instead of 64)
|
||||
|
||||
2013/05/14:
|
||||
Update upnpreplyparse to take into account "empty" elements
|
||||
validate upnpreplyparse.c code with "make check"
|
||||
|
||||
2013/05/03:
|
||||
Fix Solaris build thanks to Maciej Małecki
|
||||
|
||||
2013/04/27:
|
||||
Fix testminiwget.sh for BSD
|
||||
|
||||
2013/03/23:
|
||||
Fixed Makefile for *BSD
|
||||
|
||||
2013/03/11:
|
||||
Update Makefile to use JNAerator version 0.11
|
||||
|
||||
2013/02/11:
|
||||
Fix testminiwget.sh for use with dash
|
||||
Use $(DESTDIR) in Makefile
|
||||
|
||||
VERSION 1.8 : released 2013/02/06
|
||||
|
||||
2012/10/16:
|
||||
fix testminiwget with no IPv6 support
|
||||
|
||||
2012/09/27:
|
||||
Rename all include guards to not clash with C99
|
||||
(7.1.3 Reserved identifiers).
|
||||
|
||||
2012/08/30:
|
||||
Added -e option to upnpc program (set description for port mappings)
|
||||
|
||||
2012/08/29:
|
||||
Python 3 support (thanks to Christopher Foo)
|
||||
|
||||
2012/08/11:
|
||||
Fix a memory link in UPNP_GetValidIGD()
|
||||
Try to handle scope id in link local IPv6 URL under MS Windows
|
||||
|
||||
2012/07/20:
|
||||
Disable HAS_IP_MREQN on DragonFly BSD
|
||||
|
||||
2012/06/28:
|
||||
GetUPNPUrls() now inserts scope into link-local IPv6 addresses
|
||||
|
||||
2012/06/23:
|
||||
More error return checks in upnpc.c
|
||||
#define MINIUPNPC_GET_SRC_ADDR enables receivedata() to get scope_id
|
||||
parseURL() now parses IPv6 addresses scope
|
||||
new parameter for miniwget() : IPv6 address scope
|
||||
increment API_VERSION to 9
|
||||
|
||||
2012/06/20:
|
||||
fixed CMakeLists.txt
|
||||
|
||||
2012/05/29
|
||||
Improvements in testminiwget.sh
|
||||
|
||||
VERSION 1.7 : released 2012/05/24
|
||||
|
||||
2012/05/01:
|
||||
Cleanup settings of CFLAGS in Makefile
|
||||
Fix signed/unsigned integer comparaisons
|
||||
|
||||
2012/04/20:
|
||||
Allow to specify protocol with TCP or UDP for -A option
|
||||
|
||||
2012/04/09:
|
||||
Only try to fetch XML description once in UPNP_GetValidIGD()
|
||||
Added -ansi flag to compilation, and fixed C++ comments to ANSI C comments.
|
||||
|
||||
2012/04/05:
|
||||
minor improvements to minihttptestserver.c
|
||||
|
||||
2012/03/15:
|
||||
upnperrors.c returns valid error string for unrecognized error codes
|
||||
|
||||
2012/03/08:
|
||||
make minihttptestserver listen on loopback interface instead of 0.0.0.0
|
||||
|
||||
2012/01/25:
|
||||
Maven installation thanks to Alexey Kuznetsov
|
||||
|
||||
2012/01/21:
|
||||
Replace WIN32 macro by _WIN32
|
||||
|
||||
2012/01/19:
|
||||
Fixes in java wrappers thanks to Alexey Kuznetsov :
|
||||
https://github.com/axet/miniupnp/tree/fix-javatest/miniupnpc
|
||||
Make and install .deb packages (python) thanks to Alexey Kuznetsov :
|
||||
https://github.com/axet/miniupnp/tree/feature-debbuild/miniupnpc
|
||||
|
||||
2012/01/07:
|
||||
The multicast interface can now be specified by name with IPv4.
|
||||
|
||||
2012/01/02:
|
||||
Install man page
|
||||
|
||||
2011/11/25:
|
||||
added header to Port Mappings list in upnpc.c
|
||||
|
||||
2011/10/09:
|
||||
Makefile : make clean now removes jnaerator generated files.
|
||||
MINIUPNPC_VERSION in miniupnpc.h (updated by make)
|
||||
|
||||
2011/09/12:
|
||||
added rootdescURL to UPNPUrls structure.
|
||||
|
||||
VERSION 1.6 : released 2011/07/25
|
||||
|
||||
2011/07/25:
|
||||
Update doc for version 1.6 release
|
||||
|
||||
2011/06/18:
|
||||
Fix for windows in miniwget.c
|
||||
|
||||
2011/06/04:
|
||||
display remote host in port mapping listing
|
||||
|
||||
2011/06/03:
|
||||
Fix in make install : there were missing headers
|
||||
|
||||
2011/05/26:
|
||||
Fix the socket leak in miniwget thanks to Richard Marsh.
|
||||
Permit to add leaseduration in -a command. Display lease duration.
|
||||
|
||||
2011/05/15:
|
||||
Try both LinkLocal and SiteLocal multicast address for SSDP in IPv6
|
||||
|
||||
2011/05/09:
|
||||
add a test in testminiwget.sh.
|
||||
more error checking in miniwget.c
|
||||
|
||||
2011/05/06:
|
||||
Adding some tool to test and validate miniwget.c
|
||||
simplified and debugged miniwget.c
|
||||
|
||||
2011/04/11:
|
||||
moving ReceiveData() to a receivedata.c file.
|
||||
parsing presentation url
|
||||
adding IGD v2 WANIPv6FirewallControl commands
|
||||
|
||||
2011/04/10:
|
||||
update of miniupnpcmodule.c
|
||||
comments in miniwget.c, update in testminiwget
|
||||
Adding errors codes from IGD v2
|
||||
new functions in upnpc.c for IGD v2
|
||||
|
||||
2011/04/09:
|
||||
Support for litteral ip v6 address in miniwget
|
||||
|
||||
2011/04/08:
|
||||
Adding support for urn:schemas-upnp-org:service:WANIPv6FirewallControl:1
|
||||
Updating APIVERSION
|
||||
Supporting IPV6 in upnpDiscover()
|
||||
Adding a -6 option to upnpc command line tool
|
||||
|
||||
2011/03/18:
|
||||
miniwget/parseURL() : return an error when url param is null.
|
||||
fixing GetListOfPortMappings()
|
||||
|
||||
2011/03/14:
|
||||
upnpDiscover() now reporting an error code.
|
||||
improvements in comments.
|
||||
|
||||
2011/03/11:
|
||||
adding miniupnpcstrings.h.cmake and CMakeLists.txt files.
|
||||
|
||||
2011/02/15:
|
||||
Implementation of GetListOfPortMappings()
|
||||
|
||||
2011/02/07:
|
||||
updates to minixml to support character data starting with spaces
|
||||
minixml now support CDATA
|
||||
upnpreplyparse treats <NewPortListing> specificaly
|
||||
change in simpleUPnPcommand to return the buffer (simplification)
|
||||
|
||||
2011/02/06:
|
||||
Added leaseDuration argument to AddPortMapping()
|
||||
Starting to implement GetListOfPortMappings()
|
||||
|
||||
2011/01/11:
|
||||
updating wingenminiupnpcstrings.c
|
||||
|
||||
2011/01/04:
|
||||
improving updateminiupnpcstrings.sh
|
||||
|
||||
VERSION 1.5 : released 2011/01/01
|
||||
|
||||
2010/12/21:
|
||||
use NO_GETADDRINFO macro to disable the use of getaddrinfo/freeaddrinfo
|
||||
|
||||
2010/12/11:
|
||||
Improvements on getHTTPResponse() code.
|
||||
|
||||
2010/12/09:
|
||||
new code for miniwget that handle Chunked transfer encoding
|
||||
using getHTTPResponse() in SOAP call code
|
||||
Adding MANIFEST.in for 'python setup.py bdist_rpm'
|
||||
|
||||
2010/11/25:
|
||||
changes to minissdpc.c to compile under Win32.
|
||||
see http://miniupnp.tuxfamily.org/forum/viewtopic.php?t=729
|
||||
|
||||
2010/09/17:
|
||||
Various improvement to Makefile from Michał Górny
|
||||
|
||||
2010/08/05:
|
||||
Adding the script "external-ip.sh" from Reuben Hawkins
|
||||
|
||||
2010/06/09:
|
||||
update to python module to match modification made on 2010/04/05
|
||||
update to Java test code to match modification made on 2010/04/05
|
||||
all UPNP_* function now return an error if the SOAP request failed
|
||||
at HTTP level.
|
||||
|
||||
2010/04/17:
|
||||
Using GetBestRoute() under win32 in order to find the
|
||||
right interface to use.
|
||||
|
||||
2010/04/12:
|
||||
Retrying with HTTP/1.1 if HTTP/1.0 failed. see
|
||||
http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1703
|
||||
|
||||
2010/04/07:
|
||||
avoid returning duplicates in upnpDiscover()
|
||||
|
||||
2010/04/05:
|
||||
Create a connecthostport.h/.c with connecthostport() function
|
||||
and use it in miniwget and miniupnpc.
|
||||
Use getnameinfo() instead of inet_ntop or inet_ntoa
|
||||
Work to make miniupnpc IPV6 compatible...
|
||||
Add java test code.
|
||||
Big changes in order to support device having both WANIPConnection
|
||||
and WANPPPConnection.
|
||||
|
||||
2010/04/04:
|
||||
Use getaddrinfo() instead of gethostbyname() in miniwget.
|
||||
|
||||
2010/01/06:
|
||||
#define _DARWIN_C_SOURCE for Mac OS X
|
||||
|
||||
2009/12/19:
|
||||
Improve MinGW32 build
|
||||
|
||||
2009/12/11:
|
||||
adding a MSVC9 project to build the static library and executable
|
||||
|
||||
2009/12/10:
|
||||
Fixing some compilation stuff for Windows/MinGW
|
||||
|
||||
2009/12/07:
|
||||
adaptations in Makefile and updateminiupnpcstring.sh for AmigaOS
|
||||
some fixes for Windows when using virtual ethernet adapters (it is the
|
||||
case with VMWare installed).
|
||||
|
||||
2009/12/04:
|
||||
some fixes for AmigaOS compilation
|
||||
Changed HTTP version to HTTP/1.0 for Soap too (to prevent chunked
|
||||
transfer encoding)
|
||||
|
||||
2009/12/03:
|
||||
updating printIDG and testigddescparse.c for debug.
|
||||
modifications to compile under AmigaOS
|
||||
adding a testminiwget program
|
||||
Changed miniwget to advertise itself as HTTP/1.0 to prevent chunked
|
||||
transfer encoding
|
||||
|
||||
2009/11/26:
|
||||
fixing updateminiupnpcstrings.sh to take into account
|
||||
which command that does not return an error code.
|
||||
|
||||
VERSION 1.4 : released 2009/10/30
|
||||
|
||||
2009/10/16:
|
||||
using Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS in python module.
|
||||
|
||||
2009/10/10:
|
||||
Some fixes for compilation under Solaris
|
||||
compilation fixes : http://miniupnp.tuxfamily.org/forum/viewtopic.php?p=1464
|
||||
|
||||
2009/09/21:
|
||||
fixing the code to ignore EINTR during connect() calls.
|
||||
|
||||
2009/08/07:
|
||||
Set socket timeout for connect()
|
||||
Some cleanup in miniwget.c
|
||||
|
||||
2009/08/04:
|
||||
remove multiple redirections with -d in upnpc.c
|
||||
Print textual error code in upnpc.c
|
||||
Ignore EINTR during the connect() and poll() calls.
|
||||
|
||||
2009/07/29:
|
||||
fix in updateminiupnpcstrings.sh if OS name contains "/"
|
||||
Sending a correct value for MX: field in SSDP request
|
||||
|
||||
2009/07/20:
|
||||
Change the Makefile to compile under Mac OS X
|
||||
Fixed a stackoverflow in getDevicesFromMiniSSDPD()
|
||||
|
||||
2009/07/09:
|
||||
Compile under Haiku
|
||||
generate miniupnpcstrings.h.in from miniupnpcstrings.h
|
||||
|
||||
2009/06/04:
|
||||
patching to compile under CygWin and cross compile for minGW
|
||||
|
||||
VERSION 1.3 :
|
||||
|
||||
2009/04/17:
|
||||
updating python module
|
||||
Use strtoull() when using C99
|
||||
|
||||
2009/02/28:
|
||||
Fixed miniwget.c for compiling under sun
|
||||
|
||||
2008/12/18:
|
||||
cleanup in Makefile (thanks to Paul de Weerd)
|
||||
minissdpc.c : win32 compatibility
|
||||
miniupnpc.c : changed xmlns prefix from 'm' to 'u'
|
||||
Removed NDEBUG (using DEBUG)
|
||||
|
||||
2008/10/14:
|
||||
Added the ExternalHost argument to DeletePortMapping()
|
||||
|
||||
2008/10/11:
|
||||
Added the ExternalHost argument to AddPortMapping()
|
||||
Put a correct User-Agent: header in HTTP requests.
|
||||
|
||||
VERSION 1.2 :
|
||||
|
||||
2008/10/07:
|
||||
Update docs
|
||||
|
||||
2008/09/25:
|
||||
Integrated sameport patch from Dario Meloni : Added a "sameport"
|
||||
argument to upnpDiscover().
|
||||
|
||||
2008/07/18:
|
||||
small modif to make Clang happy :)
|
||||
|
||||
2008/07/17:
|
||||
#define SOAPPREFIX "s" in miniupnpc.c in order to remove SOAP-ENV...
|
||||
|
||||
2008/07/14:
|
||||
include declspec.h in installation (to /usr/include/miniupnpc)
|
||||
|
||||
VERSION 1.1 :
|
||||
|
||||
2008/07/04:
|
||||
standard options for install/ln instead of gnu-specific stuff.
|
||||
|
||||
2008/07/03:
|
||||
now builds a .dll and .lib with win32. (mingw32)
|
||||
|
||||
2008/04/28:
|
||||
make install now install the binary of the upnpc tool
|
||||
|
||||
2008/04/27:
|
||||
added testupnpigd.py
|
||||
added error strings for miniupnpc "internal" errors
|
||||
improved python module error/exception reporting.
|
||||
|
||||
2008/04/23:
|
||||
Completely rewrite igd_desc_parse.c in order to be compatible with
|
||||
Linksys WAG200G
|
||||
Added testigddescparse
|
||||
updated python module
|
||||
|
||||
VERSION 1.0 :
|
||||
|
||||
2008/02/21:
|
||||
put some #ifdef DEBUG around DisplayNameValueList()
|
||||
|
||||
2008/02/18:
|
||||
Improved error reporting in upnpcommands.c
|
||||
UPNP_GetStatusInfo() returns LastConnectionError
|
||||
|
||||
2008/02/16:
|
||||
better error handling in minisoap.c
|
||||
improving display of "valid IGD found" in upnpc.c
|
||||
|
||||
2008/02/03:
|
||||
Fixing UPNP_GetValidIGD()
|
||||
improved make install :)
|
||||
|
||||
2007/12/22:
|
||||
Adding upnperrors.c/h to provide a strupnperror() function
|
||||
used to translate UPnP error codes to string.
|
||||
|
||||
2007/12/19:
|
||||
Fixing getDevicesFromMiniSSDPD()
|
||||
improved error reporting of UPnP functions
|
||||
|
||||
2007/12/18:
|
||||
It is now possible to specify a different location for MiniSSDPd socket.
|
||||
working with MiniSSDPd is now more efficient.
|
||||
python module improved.
|
||||
|
||||
2007/12/16:
|
||||
improving error reporting
|
||||
|
||||
2007/12/13:
|
||||
Try to improve compatibility by using HTTP/1.0 instead of 1.1 and
|
||||
XML a bit different for SOAP.
|
||||
|
||||
2007/11/25:
|
||||
fixed select() call for linux
|
||||
|
||||
2007/11/15:
|
||||
Added -fPIC to CFLAG for better shared library code.
|
||||
|
||||
2007/11/02:
|
||||
Fixed a potential socket leak in miniwget2()
|
||||
|
||||
2007/10/16:
|
||||
added a parameter to upnpDiscover() in order to allow the use of another
|
||||
interface than the default multicast interface.
|
||||
|
||||
2007/10/12:
|
||||
Fixed the creation of symbolic link in Makefile
|
||||
|
||||
2007/10/08:
|
||||
Added man page
|
||||
|
||||
2007/10/02:
|
||||
fixed memory bug in GetUPNPUrls()
|
||||
|
||||
2007/10/01:
|
||||
fixes in the Makefile
|
||||
Added UPNP_GetIGDFromUrl() and adapted the sample program accordingly.
|
||||
Added SONAME in the shared library to please debian :)
|
||||
fixed MS Windows compilation (minissdpd is not available under MS Windows).
|
||||
|
||||
2007/09/25:
|
||||
small change to Makefile to be able to install in a different location
|
||||
(default is /usr)
|
||||
|
||||
2007/09/24:
|
||||
now compiling both shared and static library
|
||||
|
||||
2007/09/19:
|
||||
Cosmetic changes on upnpc.c
|
||||
|
||||
2007/09/02:
|
||||
adapting to new miniSSDPd (release version ?)
|
||||
|
||||
2007/08/31:
|
||||
Usage of miniSSDPd to skip discovery process.
|
||||
|
||||
2007/08/27:
|
||||
fixed python module to allow compilation with Python older than Python 2.4
|
||||
|
||||
2007/06/12:
|
||||
Added a python module.
|
||||
|
||||
2007/05/19:
|
||||
Fixed compilation under MinGW
|
||||
|
||||
2007/05/15:
|
||||
fixed a memory leak in AddPortMapping()
|
||||
Added testupnpreplyparse executable to check the parsing of
|
||||
upnp soap messages
|
||||
minixml now ignore namespace prefixes.
|
||||
|
||||
2007/04/26:
|
||||
upnpc now displays external ip address with -s or -l
|
||||
|
||||
2007/04/11:
|
||||
changed MINIUPNPC_URL_MAXSIZE to 128 to accomodate the "BT Voyager 210"
|
||||
|
||||
2007/03/19:
|
||||
cleanup in miniwget.c
|
||||
|
||||
2007/03/01:
|
||||
Small typo fix...
|
||||
|
||||
2007/01/30:
|
||||
Now parsing the HTTP header from SOAP responses in order to
|
||||
get content-length value.
|
||||
|
||||
2007/01/29:
|
||||
Fixed the Soap Query to speedup the HTTP request.
|
||||
added some Win32 DLL stuff...
|
||||
|
||||
2007/01/27:
|
||||
Fixed some WIN32 compatibility issues
|
||||
|
||||
2006/12/14:
|
||||
Added UPNPIGD_IsConnected() function in miniupnp.c/.h
|
||||
Added UPNP_GetValidIGD() in miniupnp.c/.h
|
||||
cleaned upnpc.c main(). now using UPNP_GetValidIGD()
|
||||
|
||||
2006/12/07:
|
||||
Version 1.0-RC1 released
|
||||
|
||||
2006/12/03:
|
||||
Minor changes to compile under SunOS/Solaris
|
||||
|
||||
2006/11/30:
|
||||
made a minixml parser validator program
|
||||
updated minixml to handle attributes correctly
|
||||
|
||||
2006/11/22:
|
||||
Added a -r option to the upnpc sample thanks to Alexander Hubmann.
|
||||
|
||||
2006/11/19:
|
||||
Cleanup code to make it more ANSI C compliant
|
||||
|
||||
2006/11/10:
|
||||
detect and display local lan address.
|
||||
|
||||
2006/11/04:
|
||||
Packets and Bytes Sent/Received are now unsigned int.
|
||||
|
||||
2006/11/01:
|
||||
Bug fix thanks to Giuseppe D'Angelo
|
||||
|
||||
2006/10/31:
|
||||
C++ compatibility for .h files.
|
||||
Added a way to get ip Address on the LAN used to reach the IGD.
|
||||
|
||||
2006/10/25:
|
||||
Added M-SEARCH to the services in the discovery process.
|
||||
|
||||
2006/10/22:
|
||||
updated the Makefile to use makedepend, added a "make install"
|
||||
update Makefile
|
||||
|
||||
2006/10/20:
|
||||
fixing the description url parsing thanks to patch sent by
|
||||
Wayne Dawe.
|
||||
Fixed/translated some comments.
|
||||
Implemented a better discover process, first looking
|
||||
for IGD then for root devices (as some devices only reply to
|
||||
M-SEARCH for root devices).
|
||||
|
||||
2006/09/02:
|
||||
added freeUPNPDevlist() function.
|
||||
|
||||
2006/08/04:
|
||||
More command line arguments checking
|
||||
|
||||
2006/08/01:
|
||||
Added the .bat file to compile under Win32 with minGW32
|
||||
|
||||
2006/07/31:
|
||||
Fixed the rootdesc parser (igd_desc_parse.c)
|
||||
|
||||
2006/07/20:
|
||||
parseMSEARCHReply() is now returning the ST: line as well
|
||||
starting changes to detect several UPnP devices on the network
|
||||
|
||||
2006/07/19:
|
||||
using GetCommonLinkProperties to get down/upload bitrate
|
||||
|
27
deps/miniupnpc/LICENSE
vendored
Normal file
27
deps/miniupnpc/LICENSE
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
MiniUPnPc
|
||||
Copyright (c) 2005-2016, Thomas BERNARD
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
5
deps/miniupnpc/MANIFEST.in
vendored
Normal file
5
deps/miniupnpc/MANIFEST.in
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
include README
|
||||
include miniupnpcmodule.c
|
||||
include setup.py
|
||||
include *.h
|
||||
include libminiupnpc.a
|
274
deps/miniupnpc/Makefile
vendored
Normal file
274
deps/miniupnpc/Makefile
vendored
Normal file
@ -0,0 +1,274 @@
|
||||
# $Id: Makefile,v 1.134 2016/10/07 09:04:36 nanard Exp $
|
||||
# MiniUPnP Project
|
||||
# http://miniupnp.free.fr/
|
||||
# http://miniupnp.tuxfamily.org/
|
||||
# https://github.com/miniupnp/miniupnp
|
||||
# (c) 2005-2016 Thomas Bernard
|
||||
# to install use :
|
||||
# $ make DESTDIR=/tmp/dummylocation install
|
||||
# or
|
||||
# $ INSTALLPREFIX=/usr/local make install
|
||||
# or
|
||||
# $ make install (default INSTALLPREFIX is /usr)
|
||||
OS = $(shell uname -s)
|
||||
VERSION = $(shell cat VERSION)
|
||||
|
||||
ifeq ($(OS), Darwin)
|
||||
JARSUFFIX=mac
|
||||
LIBTOOL ?= $(shell which libtool)
|
||||
endif
|
||||
ifeq ($(OS), Linux)
|
||||
JARSUFFIX=linux
|
||||
endif
|
||||
ifneq (,$(findstring NT-5.1,$(OS)))
|
||||
JARSUFFIX=win32
|
||||
endif
|
||||
|
||||
HAVE_IPV6 ?= yes
|
||||
export HAVE_IPV6
|
||||
|
||||
CC ?= gcc
|
||||
#AR = gar
|
||||
#CFLAGS = -O -g -DDEBUG
|
||||
CFLAGS ?= -O
|
||||
CFLAGS += -Wall
|
||||
CFLAGS += -W -Wstrict-prototypes
|
||||
CFLAGS += -fno-common
|
||||
CFLAGS += -DMINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
CFLAGS += -DMINIUPNPC_GET_SRC_ADDR
|
||||
CFLAGS += -D_BSD_SOURCE
|
||||
CFLAGS += -D_DEFAULT_SOURCE
|
||||
ifeq ($(OS), NetBSD)
|
||||
CFLAGS += -D_NETBSD_SOURCE
|
||||
endif
|
||||
ifneq ($(OS), FreeBSD)
|
||||
ifneq ($(OS), Darwin)
|
||||
#CFLAGS += -D_POSIX_C_SOURCE=200112L
|
||||
CFLAGS += -D_XOPEN_SOURCE=600
|
||||
endif
|
||||
endif
|
||||
#CFLAGS += -ansi
|
||||
# -DNO_GETADDRINFO
|
||||
INSTALL = install
|
||||
SH = /bin/sh
|
||||
|
||||
ifeq (SunOS, $(OS))
|
||||
LDLIBS=-lsocket -lnsl -lresolv
|
||||
CFLAGS += -D__EXTENSIONS__
|
||||
CFLAGS += -std=c99
|
||||
endif
|
||||
|
||||
# APIVERSION is used to build SONAME
|
||||
APIVERSION = 16
|
||||
|
||||
SRCS = igd_desc_parse.c miniupnpc.c minixml.c minisoap.c miniwget.c \
|
||||
upnpcommands.c upnpreplyparse.c \
|
||||
minixmlvalid.c minissdpc.c \
|
||||
upnperrors.c \
|
||||
connecthostport.c portlistingparse.c receivedata.c \
|
||||
upnpdev.c miniupnpcmodule.c
|
||||
|
||||
LIBOBJS = miniwget.o minixml.o igd_desc_parse.o minisoap.o \
|
||||
miniupnpc.o upnpreplyparse.o upnpcommands.o upnperrors.o \
|
||||
connecthostport.o portlistingparse.o receivedata.o upnpdev.o
|
||||
|
||||
ifneq ($(OS), AmigaOS)
|
||||
ifeq (,$(findstring CYGWIN,$(OS)))
|
||||
CFLAGS := -fPIC $(CFLAGS)
|
||||
endif
|
||||
LIBOBJS := $(LIBOBJS) minissdpc.o
|
||||
endif
|
||||
|
||||
OBJS = $(patsubst %.c,%.o,$(SRCS))
|
||||
|
||||
# HEADERS to install
|
||||
HEADERS = miniupnpc.h miniwget.h upnpcommands.h igd_desc_parse.h \
|
||||
upnpreplyparse.h upnperrors.h miniupnpctypes.h \
|
||||
portlistingparse.h \
|
||||
upnpdev.h \
|
||||
miniupnpc_declspec.h
|
||||
|
||||
# library names
|
||||
LIBRARY = libminiupnpc.a
|
||||
ifeq ($(OS), Darwin)
|
||||
SHAREDLIBRARY = libminiupnpc.dylib
|
||||
SONAME = $(basename $(SHAREDLIBRARY)).$(APIVERSION).dylib
|
||||
CFLAGS := -D_DARWIN_C_SOURCE $(CFLAGS)
|
||||
else
|
||||
ifeq ($(JARSUFFIX), win32)
|
||||
SHAREDLIBRARY = miniupnpc.dll
|
||||
else
|
||||
# Linux/BSD/etc.
|
||||
SHAREDLIBRARY = libminiupnpc.so
|
||||
SONAME = $(SHAREDLIBRARY).$(APIVERSION)
|
||||
endif
|
||||
endif
|
||||
|
||||
EXECUTABLES = upnpc-static
|
||||
|
||||
ifneq ($(OS), AmigaOS)
|
||||
EXECUTABLES := $(EXECUTABLES) upnpc-shared
|
||||
endif
|
||||
|
||||
LIBDIR ?= lib
|
||||
# install directories
|
||||
ifeq ($(strip $(PREFIX)),)
|
||||
INSTALLPREFIX ?= /usr
|
||||
else
|
||||
INSTALLPREFIX ?= $(PREFIX)
|
||||
endif
|
||||
INSTALLDIRINC = $(INSTALLPREFIX)/include/miniupnpc
|
||||
INSTALLDIRLIB = $(INSTALLPREFIX)/$(LIBDIR)
|
||||
INSTALLDIRBIN = $(INSTALLPREFIX)/bin
|
||||
INSTALLDIRMAN = $(INSTALLPREFIX)/share/man
|
||||
|
||||
FILESTOINSTALL = $(LIBRARY) $(EXECUTABLES)
|
||||
ifneq ($(OS), AmigaOS)
|
||||
FILESTOINSTALL := $(FILESTOINSTALL) $(SHAREDLIBRARY)
|
||||
endif
|
||||
|
||||
|
||||
.PHONY: install clean depend all check test everything \
|
||||
updateversion
|
||||
# validateminixml validateminiwget
|
||||
|
||||
all: $(LIBRARY) $(EXECUTABLES)
|
||||
|
||||
test: check
|
||||
|
||||
check: validateminixml validateminiwget validateupnpreplyparse \
|
||||
validateportlistingparse validateigddescparse
|
||||
|
||||
everything: all
|
||||
|
||||
validateminixml: minixmlvalid
|
||||
@echo "minixml validation test"
|
||||
./minixmlvalid
|
||||
touch $@
|
||||
|
||||
validateminiwget: testminiwget minihttptestserver testminiwget.sh
|
||||
@echo "miniwget validation test"
|
||||
./testminiwget.sh
|
||||
touch $@
|
||||
|
||||
validateupnpreplyparse: testupnpreplyparse testupnpreplyparse.sh
|
||||
@echo "upnpreplyparse validation test"
|
||||
./testupnpreplyparse.sh
|
||||
touch $@
|
||||
|
||||
validateportlistingparse: testportlistingparse
|
||||
@echo "portlistingparse validation test"
|
||||
./testportlistingparse
|
||||
touch $@
|
||||
|
||||
validateigddescparse: testigddescparse
|
||||
@echo "igd desc parse validation test"
|
||||
./testigddescparse testdesc/new_LiveBox_desc.xml testdesc/new_LiveBox_desc.values
|
||||
./testigddescparse testdesc/linksys_WAG200G_desc.xml testdesc/linksys_WAG200G_desc.values
|
||||
touch $@
|
||||
|
||||
clean:
|
||||
$(RM) $(LIBRARY) $(SHAREDLIBRARY) $(EXECUTABLES) $(OBJS) miniupnpcstrings.h
|
||||
|
||||
distclean: clean
|
||||
|
||||
updateversion: miniupnpc.h
|
||||
cp miniupnpc.h miniupnpc.h.bak
|
||||
sed 's/\(.*MINIUPNPC_API_VERSION\s\+\)[0-9]\+/\1$(APIVERSION)/' < miniupnpc.h.bak > miniupnpc.h
|
||||
|
||||
install: updateversion $(FILESTOINSTALL)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
|
||||
ifneq ($(OS), AmigaOS)
|
||||
$(INSTALL) -m 644 $(SHAREDLIBRARY) $(DESTDIR)$(INSTALLDIRLIB)/$(SONAME)
|
||||
ln -fs $(SONAME) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
|
||||
endif
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
|
||||
ifeq ($(OS), AmigaOS)
|
||||
$(INSTALL) -m 755 upnpc-static $(DESTDIR)$(INSTALLDIRBIN)/upnpc
|
||||
else
|
||||
$(INSTALL) -m 755 upnpc-shared $(DESTDIR)$(INSTALLDIRBIN)/upnpc
|
||||
endif
|
||||
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
|
||||
ifneq ($(OS), AmigaOS)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRMAN)/man3
|
||||
$(INSTALL) -m 644 man3/miniupnpc.3 $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
|
||||
ifeq ($(OS), Linux)
|
||||
gzip -f $(DESTDIR)$(INSTALLDIRMAN)/man3/miniupnpc.3
|
||||
endif
|
||||
endif
|
||||
|
||||
install-static: updateversion $(FILESTOINSTALL)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -m 644 $(HEADERS) $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -m 644 $(LIBRARY) $(DESTDIR)$(INSTALLDIRLIB)
|
||||
$(INSTALL) -d $(DESTDIR)$(INSTALLDIRBIN)
|
||||
$(INSTALL) -m 755 external-ip.sh $(DESTDIR)$(INSTALLDIRBIN)/external-ip
|
||||
|
||||
cleaninstall:
|
||||
$(RM) -r $(DESTDIR)$(INSTALLDIRINC)
|
||||
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(LIBRARY)
|
||||
$(RM) $(DESTDIR)$(INSTALLDIRLIB)/$(SHAREDLIBRARY)
|
||||
|
||||
depend:
|
||||
makedepend -Y -- $(CFLAGS) -- $(SRCS) 2>/dev/null
|
||||
|
||||
$(LIBRARY): $(LIBOBJS)
|
||||
ifeq ($(OS), Darwin)
|
||||
$(LIBTOOL) -static -o $@ $?
|
||||
else
|
||||
$(AR) crs $@ $?
|
||||
endif
|
||||
|
||||
$(SHAREDLIBRARY): $(LIBOBJS)
|
||||
ifeq ($(OS), Darwin)
|
||||
# $(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(SONAME) -o $@ $^
|
||||
$(CC) -dynamiclib $(LDFLAGS) -Wl,-install_name,$(INSTALLDIRLIB)/$(SONAME) -o $@ $^
|
||||
else
|
||||
$(CC) -shared $(LDFLAGS) -Wl,-soname,$(SONAME) -o $@ $^
|
||||
endif
|
||||
|
||||
upnpc-static: $(LIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
upnpc-shared: $(SHAREDLIBRARY)
|
||||
$(CC) $(LDFLAGS) -o $@ $^ $(LOADLIBES) $(LDLIBS)
|
||||
|
||||
minixmlvalid: minixml.o minixmlvalid.o
|
||||
|
||||
miniupnpcstrings.h: miniupnpcstrings.h.in updateminiupnpcstrings.sh VERSION
|
||||
$(SH) updateminiupnpcstrings.sh
|
||||
|
||||
# DO NOT DELETE THIS LINE -- make depend depends on it.
|
||||
|
||||
igd_desc_parse.o: igd_desc_parse.h
|
||||
miniupnpc.o: miniupnpc.h miniupnpc_declspec.h igd_desc_parse.h upnpdev.h
|
||||
miniupnpc.o: minissdpc.h miniwget.h minisoap.h minixml.h upnpcommands.h
|
||||
miniupnpc.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h
|
||||
miniupnpc.o: connecthostport.h
|
||||
minixml.o: minixml.h
|
||||
minisoap.o: minisoap.h miniupnpcstrings.h
|
||||
miniwget.o: miniupnpcstrings.h miniwget.h miniupnpc_declspec.h
|
||||
miniwget.o: connecthostport.h receivedata.h
|
||||
upnpcommands.o: upnpcommands.h upnpreplyparse.h portlistingparse.h
|
||||
upnpcommands.o: miniupnpc_declspec.h miniupnpctypes.h miniupnpc.h
|
||||
upnpcommands.o: igd_desc_parse.h upnpdev.h
|
||||
upnpreplyparse.o: upnpreplyparse.h minixml.h
|
||||
minixmlvalid.o: minixml.h
|
||||
minissdpc.o: minissdpc.h miniupnpc_declspec.h upnpdev.h miniupnpc.h
|
||||
minissdpc.o: igd_desc_parse.h receivedata.h codelength.h
|
||||
upnperrors.o: upnperrors.h miniupnpc_declspec.h upnpcommands.h
|
||||
upnperrors.o: upnpreplyparse.h portlistingparse.h miniupnpctypes.h
|
||||
upnperrors.o: miniupnpc.h igd_desc_parse.h upnpdev.h
|
||||
connecthostport.o: connecthostport.h
|
||||
portlistingparse.o: portlistingparse.h miniupnpc_declspec.h miniupnpctypes.h
|
||||
portlistingparse.o: minixml.h
|
||||
receivedata.o: receivedata.h
|
||||
upnpdev.o: upnpdev.h miniupnpc_declspec.h
|
||||
testportlistingparse.o: miniupnpctypes.h
|
||||
miniupnpcmodule.o: miniupnpc.h miniupnpc_declspec.h igd_desc_parse.h
|
||||
miniupnpcmodule.o: upnpdev.h upnpcommands.h upnpreplyparse.h
|
||||
miniupnpcmodule.o: portlistingparse.h miniupnpctypes.h upnperrors.h
|
64
deps/miniupnpc/README
vendored
Normal file
64
deps/miniupnpc/README
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
Project: miniupnp
|
||||
Project web page: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
github: https://github.com/miniupnp/miniupnp
|
||||
freecode: http://freecode.com/projects/miniupnp
|
||||
Author: Thomas Bernard
|
||||
Copyright (c) 2005-2016 Thomas Bernard
|
||||
This software is subject to the conditions detailed in the
|
||||
LICENSE file provided within this distribution.
|
||||
|
||||
|
||||
* miniUPnP Client - miniUPnPc *
|
||||
|
||||
To compile, simply run 'gmake' (could be 'make' on your system).
|
||||
Under win32, to compile with MinGW, type "mingw32make.bat".
|
||||
MS Visual C solution and project files are supplied in the msvc/ subdirectory.
|
||||
|
||||
The compilation is known to work under linux, FreeBSD,
|
||||
OpenBSD, MacOS X, AmigaOS and cygwin.
|
||||
The official AmigaOS4.1 SDK was used for AmigaOS4 and GeekGadgets for AmigaOS3.
|
||||
upx (http://upx.sourceforge.net) is used to compress the win32 .exe files.
|
||||
|
||||
To install the library and headers on the system use :
|
||||
> su
|
||||
> make install
|
||||
> exit
|
||||
|
||||
alternatively, to install into a specific location, use :
|
||||
> INSTALLPREFIX=/usr/local make install
|
||||
|
||||
upnpc.c is a sample client using the libminiupnpc.
|
||||
To use the libminiupnpc in your application, link it with
|
||||
libminiupnpc.a (or .so) and use the following functions found in miniupnpc.h,
|
||||
upnpcommands.h and miniwget.h :
|
||||
- upnpDiscover()
|
||||
- UPNP_GetValidIGD()
|
||||
- miniwget()
|
||||
- parserootdesc()
|
||||
- GetUPNPUrls()
|
||||
- UPNP_* (calling UPNP methods)
|
||||
|
||||
Note : use #include <miniupnpc/miniupnpc.h> etc... for the includes
|
||||
and -lminiupnpc for the link
|
||||
|
||||
Discovery process is speeded up when MiniSSDPd is running on the machine.
|
||||
|
||||
|
||||
* Python module *
|
||||
|
||||
you can build a python module with 'make pythonmodule'
|
||||
and install it with 'make installpythonmodule'.
|
||||
setup.py (and setupmingw32.py) are included in the distribution.
|
||||
|
||||
|
||||
Feel free to contact me if you have any problem :
|
||||
e-mail : miniupnp@free.fr
|
||||
|
||||
If you are using libminiupnpc in your application, please
|
||||
send me an email !
|
||||
|
||||
For any question, you can use the web forum :
|
||||
http://miniupnp.tuxfamily.org/forum/
|
||||
|
||||
Bugs should be reported on github :
|
||||
https://github.com/miniupnp/miniupnp/issues
|
1
deps/miniupnpc/VERSION
vendored
Normal file
1
deps/miniupnpc/VERSION
vendored
Normal file
@ -0,0 +1 @@
|
||||
2.0
|
172
deps/miniupnpc/apiversions.txt
vendored
Normal file
172
deps/miniupnpc/apiversions.txt
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
$Id: apiversions.txt,v 1.9 2016/01/24 17:24:36 nanard Exp $
|
||||
|
||||
Differences in API between miniUPnPc versions
|
||||
|
||||
API version 16
|
||||
added "status_code" argument to getHTTPResponse(), miniwget() and miniwget_getaddr()
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 16
|
||||
|
||||
API version 15
|
||||
changed "sameport" argument of upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
|
||||
to "localport". When 0 or 1, behaviour is not changed, but it can take
|
||||
any other value between 2 and 65535
|
||||
Existing programs should be compatible
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 15
|
||||
|
||||
API version 14
|
||||
miniupnpc.h
|
||||
add ttl argument to upnpDiscover() upnpDiscoverAll() upnpDiscoverDevice()
|
||||
upnpDiscoverDevices()
|
||||
getDevicesFromMiniSSDPD() :
|
||||
connectToMiniSSDPD() / disconnectFromMiniSSDPD()
|
||||
requestDevicesFromMiniSSDPD() / receiveDevicesFromMiniSSDPD()
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 14
|
||||
|
||||
API version 13
|
||||
miniupnpc.h:
|
||||
add searchalltype param to upnpDiscoverDevices() function
|
||||
updated macro :
|
||||
#define MINIUPNPC_API_VERSION 13
|
||||
|
||||
API version 12
|
||||
miniupnpc.h :
|
||||
add upnpDiscoverAll() / upnpDiscoverDevice() / upnpDiscoverDevices()
|
||||
functions
|
||||
updated macros :
|
||||
#define MINIUPNPC_API_VERSION 12
|
||||
|
||||
API version 11
|
||||
|
||||
upnpreplyparse.h / portlistingparse.h :
|
||||
removed usage of sys/queue.h / bsdqueue.h
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_API_VERSION 11
|
||||
|
||||
====================== miniUPnPc version 1.9 ======================
|
||||
API version 10
|
||||
|
||||
upnpcommands.h:
|
||||
added argument remoteHost to UPNP_GetSpecificPortMappingEntry()
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_VERSION "1.9"
|
||||
#define MINIUPNPC_API_VERSION 10
|
||||
|
||||
====================== miniUPnPc version 1.8 ======================
|
||||
API version 9
|
||||
|
||||
miniupnpc.h:
|
||||
updated macros :
|
||||
#define MINIUPNPC_VERSION "1.8"
|
||||
#define MINIUPNPC_API_VERSION 9
|
||||
added "unsigned int scope_id;" to struct UPNPDev
|
||||
added scope_id argument to GetUPNPUrls()
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.7 ======================
|
||||
API version 8
|
||||
|
||||
miniupnpc.h :
|
||||
add new macros :
|
||||
#define MINIUPNPC_VERSION "1.7"
|
||||
#define MINIUPNPC_API_VERSION 8
|
||||
add rootdescURL to struct UPNPUrls
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.6 ======================
|
||||
API version 8
|
||||
|
||||
Adding support for IPv6.
|
||||
igd_desc_parse.h :
|
||||
struct IGDdatas_service :
|
||||
add char presentationurl[MINIUPNPC_URL_MAXSIZE];
|
||||
struct IGDdatas :
|
||||
add struct IGDdatas_service IPv6FC;
|
||||
miniupnpc.h :
|
||||
new macros :
|
||||
#define UPNPDISCOVER_SUCCESS (0)
|
||||
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
|
||||
#define UPNPDISCOVER_SOCKET_ERROR (-101)
|
||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
simpleUPnPcommand() prototype changed (but is normaly not used by API users)
|
||||
add arguments ipv6 and error to upnpDiscover() :
|
||||
struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int sameport,
|
||||
int ipv6,
|
||||
int * error);
|
||||
add controlURL_6FC member to struct UPNPUrls :
|
||||
struct UPNPUrls {
|
||||
char * controlURL;
|
||||
char * ipcondescURL;
|
||||
char * controlURL_CIF;
|
||||
char * controlURL_6FC;
|
||||
};
|
||||
|
||||
upnpcommands.h :
|
||||
add leaseDuration argument to UPNP_AddPortMapping()
|
||||
add desc, enabled and leaseDuration arguments to UPNP_GetSpecificPortMappingEntry()
|
||||
add UPNP_GetListOfPortMappings() function (IGDv2)
|
||||
add IGDv2 IPv6 related functions :
|
||||
UPNP_GetFirewallStatus()
|
||||
UPNP_GetOutboundPinholeTimeout()
|
||||
UPNP_AddPinhole()
|
||||
UPNP_UpdatePinhole()
|
||||
UPNP_DeletePinhole()
|
||||
UPNP_CheckPinholeWorking()
|
||||
UPNP_GetPinholePackets()
|
||||
|
||||
|
||||
|
||||
====================== miniUPnPc version 1.5 ======================
|
||||
API version 5
|
||||
|
||||
new function :
|
||||
int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
|
||||
new macro in upnpcommands.h :
|
||||
#define UPNPCOMMAND_HTTP_ERROR
|
||||
|
||||
====================== miniUPnPc version 1.4 ======================
|
||||
Same API as version 1.3
|
||||
|
||||
====================== miniUPnPc version 1.3 ======================
|
||||
API version 4
|
||||
|
||||
Use UNSIGNED_INTEGER type for
|
||||
UPNP_GetTotalBytesSent(), UPNP_GetTotalBytesReceived(),
|
||||
UPNP_GetTotalPacketsSent(), UPNP_GetTotalPacketsReceived()
|
||||
Add remoteHost argument to UPNP_AddPortMapping() and UPNP_DeletePortMapping()
|
||||
|
||||
====================== miniUPnPc version 1.2 ======================
|
||||
API version 3
|
||||
|
||||
added sameport argument to upnpDiscover()
|
||||
struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int sameport);
|
||||
|
||||
====================== miniUPnPc Version 1.1 ======================
|
||||
Same API as 1.0
|
||||
|
||||
|
||||
====================== miniUPnPc Version 1.0 ======================
|
||||
API version 2
|
||||
|
||||
|
||||
struct UPNPDev {
|
||||
struct UPNPDev * pNext;
|
||||
char * descURL;
|
||||
char * st;
|
||||
char buffer[2];
|
||||
};
|
||||
struct UPNPDev * upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock);
|
||||
|
54
deps/miniupnpc/codelength.h
vendored
Normal file
54
deps/miniupnpc/codelength.h
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/* $Id: codelength.h,v 1.3 2011/07/30 13:10:05 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
#ifndef CODELENGTH_H_INCLUDED
|
||||
#define CODELENGTH_H_INCLUDED
|
||||
|
||||
/* Encode length by using 7bit per Byte :
|
||||
* Most significant bit of each byte specifies that the
|
||||
* following byte is part of the code */
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
*/
|
||||
#define DECODELENGTH(n, p) n = 0; \
|
||||
do { n = (n << 7) | (*p & 0x7f); } \
|
||||
while((*(p++)&0x80) && (n<(1<<25)));
|
||||
|
||||
/* n : unsigned
|
||||
* READ : function/macro to read one byte (unsigned char)
|
||||
*/
|
||||
#define DECODELENGTH_READ(n, READ) \
|
||||
n = 0; \
|
||||
do { \
|
||||
unsigned char c; \
|
||||
READ(c); \
|
||||
n = (n << 7) | (c & 0x07f); \
|
||||
if(!(c&0x80)) break; \
|
||||
} while(n<(1<<25));
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
* p_limit : unsigned char *
|
||||
*/
|
||||
#define DECODELENGTH_CHECKLIMIT(n, p, p_limit) \
|
||||
n = 0; \
|
||||
do { \
|
||||
if((p) >= (p_limit)) break; \
|
||||
n = (n << 7) | (*(p) & 0x7f); \
|
||||
} while((*((p)++)&0x80) && (n<(1<<25)));
|
||||
|
||||
|
||||
/* n : unsigned
|
||||
* p : unsigned char *
|
||||
*/
|
||||
#define CODELENGTH(n, p) if(n>=268435456) *(p++) = (n >> 28) | 0x80; \
|
||||
if(n>=2097152) *(p++) = (n >> 21) | 0x80; \
|
||||
if(n>=16384) *(p++) = (n >> 14) | 0x80; \
|
||||
if(n>=128) *(p++) = (n >> 7) | 0x80; \
|
||||
*(p++) = n & 0x7f;
|
||||
|
||||
#endif /* CODELENGTH_H_INCLUDED */
|
264
deps/miniupnpc/connecthostport.c
vendored
Normal file
264
deps/miniupnpc/connecthostport.c
vendored
Normal file
@ -0,0 +1,264 @@
|
||||
/* $Id: connecthostport.c,v 1.15 2015/10/09 16:26:19 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2010-2016 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
/* use getaddrinfo() or gethostbyname()
|
||||
* uncomment the following line in order to use gethostbyname() */
|
||||
#ifdef NO_GETADDRINFO
|
||||
#define USE_GETHOSTBYNAME
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#define snprintf _snprintf
|
||||
#define herror
|
||||
#define socklen_t int
|
||||
#else /* #ifdef _WIN32 */
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
#include <sys/time.h>
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
#include <sys/param.h>
|
||||
#include <sys/select.h>
|
||||
#include <errno.h>
|
||||
#define closesocket close
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
/* defining MINIUPNPC_IGNORE_EINTR enable the ignore of interruptions
|
||||
* during the connect() call */
|
||||
#define MINIUPNPC_IGNORE_EINTR
|
||||
#ifndef USE_GETHOSTBYNAME
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#endif /* #ifndef USE_GETHOSTBYNAME */
|
||||
#endif /* #else _WIN32 */
|
||||
|
||||
/* definition of PRINT_SOCKET_ERROR */
|
||||
#ifdef _WIN32
|
||||
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
||||
#else
|
||||
#define PRINT_SOCKET_ERROR(x) perror(x)
|
||||
#endif
|
||||
|
||||
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||
#define herror(A) printf("%s\n", A)
|
||||
#endif
|
||||
|
||||
#include "connecthostport.h"
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
/* connecthostport()
|
||||
* return a socket connected (TCP) to the host and port
|
||||
* or -1 in case of error */
|
||||
int connecthostport(const char * host, unsigned short port,
|
||||
unsigned int scope_id)
|
||||
{
|
||||
int s, n;
|
||||
#ifdef USE_GETHOSTBYNAME
|
||||
struct sockaddr_in dest;
|
||||
struct hostent *hp;
|
||||
#else /* #ifdef USE_GETHOSTBYNAME */
|
||||
char tmp_host[MAXHOSTNAMELEN+1];
|
||||
char port_str[8];
|
||||
struct addrinfo *ai, *p;
|
||||
struct addrinfo hints;
|
||||
#endif /* #ifdef USE_GETHOSTBYNAME */
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
struct timeval timeout;
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
|
||||
#ifdef USE_GETHOSTBYNAME
|
||||
hp = gethostbyname(host);
|
||||
if(hp == NULL)
|
||||
{
|
||||
herror(host);
|
||||
return -1;
|
||||
}
|
||||
memcpy(&dest.sin_addr, hp->h_addr, sizeof(dest.sin_addr));
|
||||
memset(dest.sin_zero, 0, sizeof(dest.sin_zero));
|
||||
s = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if(s < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
return -1;
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
/* setting a 3 seconds timeout for the connect() call */
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt SO_RCVTIMEO");
|
||||
}
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt SO_SNDTIMEO");
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
dest.sin_family = AF_INET;
|
||||
dest.sin_port = htons(port);
|
||||
n = connect(s, (struct sockaddr *)&dest, sizeof(struct sockaddr_in));
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
/* EINTR The system call was interrupted by a signal that was caught
|
||||
* EINPROGRESS The socket is nonblocking and the connection cannot
|
||||
* be completed immediately. */
|
||||
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
|
||||
{
|
||||
socklen_t len;
|
||||
fd_set wset;
|
||||
int err;
|
||||
FD_ZERO(&wset);
|
||||
FD_SET(s, &wset);
|
||||
if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
|
||||
continue;
|
||||
/*len = 0;*/
|
||||
/*n = getpeername(s, NULL, &len);*/
|
||||
len = sizeof(err);
|
||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
PRINT_SOCKET_ERROR("getsockopt");
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
if(err != 0) {
|
||||
errno = err;
|
||||
n = -1;
|
||||
}
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
|
||||
if(n<0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("connect");
|
||||
closesocket(s);
|
||||
return -1;
|
||||
}
|
||||
#else /* #ifdef USE_GETHOSTBYNAME */
|
||||
/* use getaddrinfo() instead of gethostbyname() */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
/* hints.ai_flags = AI_ADDRCONFIG; */
|
||||
#ifdef AI_NUMERICSERV
|
||||
hints.ai_flags = AI_NUMERICSERV;
|
||||
#endif
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_family = AF_UNSPEC; /* AF_INET, AF_INET6 or AF_UNSPEC */
|
||||
/* hints.ai_protocol = IPPROTO_TCP; */
|
||||
snprintf(port_str, sizeof(port_str), "%hu", port);
|
||||
if(host[0] == '[')
|
||||
{
|
||||
/* literal ip v6 address */
|
||||
int i, j;
|
||||
for(i = 0, j = 1; host[j] && (host[j] != ']') && i < MAXHOSTNAMELEN; i++, j++)
|
||||
{
|
||||
tmp_host[i] = host[j];
|
||||
if(0 == memcmp(host+j, "%25", 3)) /* %25 is just url encoding for '%' */
|
||||
j+=2; /* skip "25" */
|
||||
}
|
||||
tmp_host[i] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(tmp_host, host, MAXHOSTNAMELEN);
|
||||
}
|
||||
tmp_host[MAXHOSTNAMELEN] = '\0';
|
||||
n = getaddrinfo(tmp_host, port_str, &hints, &ai);
|
||||
if(n != 0)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
fprintf(stderr, "getaddrinfo() error : %d\n", n);
|
||||
#else
|
||||
fprintf(stderr, "getaddrinfo() error : %s\n", gai_strerror(n));
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
s = -1;
|
||||
for(p = ai; p; p = p->ai_next)
|
||||
{
|
||||
s = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if(s < 0)
|
||||
continue;
|
||||
if(p->ai_addr->sa_family == AF_INET6 && scope_id > 0) {
|
||||
struct sockaddr_in6 * addr6 = (struct sockaddr_in6 *)p->ai_addr;
|
||||
addr6->sin6_scope_id = scope_id;
|
||||
}
|
||||
#ifdef MINIUPNPC_SET_SOCKET_TIMEOUT
|
||||
/* setting a 3 seconds timeout for the connect() call */
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
}
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
n = connect(s, p->ai_addr, p->ai_addrlen);
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
/* EINTR The system call was interrupted by a signal that was caught
|
||||
* EINPROGRESS The socket is nonblocking and the connection cannot
|
||||
* be completed immediately. */
|
||||
while(n < 0 && (errno == EINTR || errno == EINPROGRESS))
|
||||
{
|
||||
socklen_t len;
|
||||
fd_set wset;
|
||||
int err;
|
||||
FD_ZERO(&wset);
|
||||
FD_SET(s, &wset);
|
||||
if((n = select(s + 1, NULL, &wset, NULL, NULL)) == -1 && errno == EINTR)
|
||||
continue;
|
||||
/*len = 0;*/
|
||||
/*n = getpeername(s, NULL, &len);*/
|
||||
len = sizeof(err);
|
||||
if(getsockopt(s, SOL_SOCKET, SO_ERROR, &err, &len) < 0) {
|
||||
PRINT_SOCKET_ERROR("getsockopt");
|
||||
closesocket(s);
|
||||
freeaddrinfo(ai);
|
||||
return -1;
|
||||
}
|
||||
if(err != 0) {
|
||||
errno = err;
|
||||
n = -1;
|
||||
}
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_IGNORE_EINTR */
|
||||
if(n < 0)
|
||||
{
|
||||
closesocket(s);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(ai);
|
||||
if(s < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
return -1;
|
||||
}
|
||||
if(n < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("connect");
|
||||
return -1;
|
||||
}
|
||||
#endif /* #ifdef USE_GETHOSTBYNAME */
|
||||
return s;
|
||||
}
|
||||
|
18
deps/miniupnpc/connecthostport.h
vendored
Normal file
18
deps/miniupnpc/connecthostport.h
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/* $Id: connecthostport.h,v 1.2 2012/06/23 22:32:33 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2010-2012 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef CONNECTHOSTPORT_H_INCLUDED
|
||||
#define CONNECTHOSTPORT_H_INCLUDED
|
||||
|
||||
/* connecthostport()
|
||||
* return a socket connected (TCP) to the host and port
|
||||
* or -1 in case of error */
|
||||
int connecthostport(const char * host, unsigned short port,
|
||||
unsigned int scope_id);
|
||||
|
||||
#endif
|
||||
|
123
deps/miniupnpc/igd_desc_parse.c
vendored
Normal file
123
deps/miniupnpc/igd_desc_parse.c
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/* $Id: igd_desc_parse.c,v 1.17 2015/09/15 13:30:04 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include "igd_desc_parse.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Start element handler :
|
||||
* update nesting level counter and copy element name */
|
||||
void IGDstartelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
if(l >= MINIUPNPC_URL_MAXSIZE)
|
||||
l = MINIUPNPC_URL_MAXSIZE-1;
|
||||
memcpy(datas->cureltname, name, l);
|
||||
datas->cureltname[l] = '\0';
|
||||
datas->level++;
|
||||
if( (l==7) && !memcmp(name, "service", l) ) {
|
||||
datas->tmp.controlurl[0] = '\0';
|
||||
datas->tmp.eventsuburl[0] = '\0';
|
||||
datas->tmp.scpdurl[0] = '\0';
|
||||
datas->tmp.servicetype[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
|
||||
|
||||
/* End element handler :
|
||||
* update nesting level counter and update parser state if
|
||||
* service element is parsed */
|
||||
void IGDendelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
datas->level--;
|
||||
/*printf("endelt %2d %.*s\n", datas->level, l, name);*/
|
||||
if( (l==7) && !memcmp(name, "service", l) )
|
||||
{
|
||||
if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:")) {
|
||||
memcpy(&datas->CIF, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPv6FirewallControl:")) {
|
||||
memcpy(&datas->IPv6FC, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else if(COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:")
|
||||
|| COMPARE(datas->tmp.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:") ) {
|
||||
if(datas->first.servicetype[0] == '\0') {
|
||||
memcpy(&datas->first, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
} else {
|
||||
memcpy(&datas->second, &datas->tmp, sizeof(struct IGDdatas_service));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Data handler :
|
||||
* copy data depending on the current element name and state */
|
||||
void IGDdata(void * d, const char * data, int l)
|
||||
{
|
||||
struct IGDdatas * datas = (struct IGDdatas *)d;
|
||||
char * dstmember = 0;
|
||||
/*printf("%2d %s : %.*s\n",
|
||||
datas->level, datas->cureltname, l, data); */
|
||||
if( !strcmp(datas->cureltname, "URLBase") )
|
||||
dstmember = datas->urlbase;
|
||||
else if( !strcmp(datas->cureltname, "presentationURL") )
|
||||
dstmember = datas->presentationurl;
|
||||
else if( !strcmp(datas->cureltname, "serviceType") )
|
||||
dstmember = datas->tmp.servicetype;
|
||||
else if( !strcmp(datas->cureltname, "controlURL") )
|
||||
dstmember = datas->tmp.controlurl;
|
||||
else if( !strcmp(datas->cureltname, "eventSubURL") )
|
||||
dstmember = datas->tmp.eventsuburl;
|
||||
else if( !strcmp(datas->cureltname, "SCPDURL") )
|
||||
dstmember = datas->tmp.scpdurl;
|
||||
/* else if( !strcmp(datas->cureltname, "deviceType") )
|
||||
dstmember = datas->devicetype_tmp;*/
|
||||
if(dstmember)
|
||||
{
|
||||
if(l>=MINIUPNPC_URL_MAXSIZE)
|
||||
l = MINIUPNPC_URL_MAXSIZE-1;
|
||||
memcpy(dstmember, data, l);
|
||||
dstmember[l] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void printIGD(struct IGDdatas * d)
|
||||
{
|
||||
printf("urlbase = '%s'\n", d->urlbase);
|
||||
printf("WAN Device (Common interface config) :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->CIF.devicetype);*/
|
||||
printf(" serviceType = '%s'\n", d->CIF.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->CIF.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->CIF.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->CIF.scpdurl);
|
||||
printf("primary WAN Connection Device (IP or PPP Connection):\n");
|
||||
/*printf(" deviceType = '%s'\n", d->first.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->first.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->first.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->first.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->first.scpdurl);
|
||||
printf("secondary WAN Connection Device (IP or PPP Connection):\n");
|
||||
/*printf(" deviceType = '%s'\n", d->second.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->second.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->second.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->second.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->second.scpdurl);
|
||||
printf("WAN IPv6 Firewall Control :\n");
|
||||
/*printf(" deviceType = '%s'\n", d->IPv6FC.devicetype);*/
|
||||
printf(" servicetype = '%s'\n", d->IPv6FC.servicetype);
|
||||
printf(" controlURL = '%s'\n", d->IPv6FC.controlurl);
|
||||
printf(" eventSubURL = '%s'\n", d->IPv6FC.eventsuburl);
|
||||
printf(" SCPDURL = '%s'\n", d->IPv6FC.scpdurl);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
49
deps/miniupnpc/igd_desc_parse.h
vendored
Normal file
49
deps/miniupnpc/igd_desc_parse.h
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
/* $Id: igd_desc_parse.h,v 1.12 2014/11/17 17:19:13 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2014 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef IGD_DESC_PARSE_H_INCLUDED
|
||||
#define IGD_DESC_PARSE_H_INCLUDED
|
||||
|
||||
/* Structure to store the result of the parsing of UPnP
|
||||
* descriptions of Internet Gateway Devices */
|
||||
#define MINIUPNPC_URL_MAXSIZE (128)
|
||||
struct IGDdatas_service {
|
||||
char controlurl[MINIUPNPC_URL_MAXSIZE];
|
||||
char eventsuburl[MINIUPNPC_URL_MAXSIZE];
|
||||
char scpdurl[MINIUPNPC_URL_MAXSIZE];
|
||||
char servicetype[MINIUPNPC_URL_MAXSIZE];
|
||||
/*char devicetype[MINIUPNPC_URL_MAXSIZE];*/
|
||||
};
|
||||
|
||||
struct IGDdatas {
|
||||
char cureltname[MINIUPNPC_URL_MAXSIZE];
|
||||
char urlbase[MINIUPNPC_URL_MAXSIZE];
|
||||
char presentationurl[MINIUPNPC_URL_MAXSIZE];
|
||||
int level;
|
||||
/*int state;*/
|
||||
/* "urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1" */
|
||||
struct IGDdatas_service CIF;
|
||||
/* "urn:schemas-upnp-org:service:WANIPConnection:1"
|
||||
* "urn:schemas-upnp-org:service:WANPPPConnection:1" */
|
||||
struct IGDdatas_service first;
|
||||
/* if both WANIPConnection and WANPPPConnection are present */
|
||||
struct IGDdatas_service second;
|
||||
/* "urn:schemas-upnp-org:service:WANIPv6FirewallControl:1" */
|
||||
struct IGDdatas_service IPv6FC;
|
||||
/* tmp */
|
||||
struct IGDdatas_service tmp;
|
||||
};
|
||||
|
||||
void IGDstartelt(void *, const char *, int);
|
||||
void IGDendelt(void *, const char *, int);
|
||||
void IGDdata(void *, const char *, int);
|
||||
#ifdef DEBUG
|
||||
void printIGD(struct IGDdatas *);
|
||||
#endif /* DEBUG */
|
||||
|
||||
#endif /* IGD_DESC_PARSE_H_INCLUDED */
|
123
deps/miniupnpc/minisoap.c
vendored
Normal file
123
deps/miniupnpc/minisoap.c
vendored
Normal file
@ -0,0 +1,123 @@
|
||||
/* $Id: minisoap.c,v 1.23 2014/11/04 22:31:55 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
*
|
||||
* Minimal SOAP implementation for UPnP protocol.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#define snprintf _snprintf
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
#include "minisoap.h"
|
||||
#include "miniupnpcstrings.h"
|
||||
|
||||
/* only for malloc */
|
||||
#include <stdlib.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
||||
#else
|
||||
#define PRINT_SOCKET_ERROR(x) perror(x)
|
||||
#endif
|
||||
|
||||
/* httpWrite sends the headers and the body to the socket
|
||||
* and returns the number of bytes sent */
|
||||
static int
|
||||
httpWrite(int fd, const char * body, int bodysize,
|
||||
const char * headers, int headerssize)
|
||||
{
|
||||
int n = 0;
|
||||
/*n = write(fd, headers, headerssize);*/
|
||||
/*if(bodysize>0)
|
||||
n += write(fd, body, bodysize);*/
|
||||
/* Note : my old linksys router only took into account
|
||||
* soap request that are sent into only one packet */
|
||||
char * p;
|
||||
/* TODO: AVOID MALLOC, we could use writev() for that */
|
||||
p = malloc(headerssize+bodysize);
|
||||
if(!p)
|
||||
return -1;
|
||||
memcpy(p, headers, headerssize);
|
||||
memcpy(p+headerssize, body, bodysize);
|
||||
/*n = write(fd, p, headerssize+bodysize);*/
|
||||
n = send(fd, p, headerssize+bodysize, 0);
|
||||
if(n<0) {
|
||||
PRINT_SOCKET_ERROR("send");
|
||||
}
|
||||
/* disable send on the socket */
|
||||
/* draytek routers dont seems to like that... */
|
||||
#if 0
|
||||
#ifdef _WIN32
|
||||
if(shutdown(fd, SD_SEND)<0) {
|
||||
#else
|
||||
if(shutdown(fd, SHUT_WR)<0) { /*SD_SEND*/
|
||||
#endif
|
||||
PRINT_SOCKET_ERROR("shutdown");
|
||||
}
|
||||
#endif
|
||||
free(p);
|
||||
return n;
|
||||
}
|
||||
|
||||
/* self explanatory */
|
||||
int soapPostSubmit(int fd,
|
||||
const char * url,
|
||||
const char * host,
|
||||
unsigned short port,
|
||||
const char * action,
|
||||
const char * body,
|
||||
const char * httpversion)
|
||||
{
|
||||
int bodysize;
|
||||
char headerbuf[512];
|
||||
int headerssize;
|
||||
char portstr[8];
|
||||
bodysize = (int)strlen(body);
|
||||
/* We are not using keep-alive HTTP connections.
|
||||
* HTTP/1.1 needs the header Connection: close to do that.
|
||||
* This is the default with HTTP/1.0
|
||||
* Using HTTP/1.1 means we need to support chunked transfer-encoding :
|
||||
* When using HTTP/1.1, the router "BiPAC 7404VNOX" always use chunked
|
||||
* transfer encoding. */
|
||||
/* Connection: Close is normally there only in HTTP/1.1 but who knows */
|
||||
portstr[0] = '\0';
|
||||
if(port != 80)
|
||||
snprintf(portstr, sizeof(portstr), ":%hu", port);
|
||||
headerssize = snprintf(headerbuf, sizeof(headerbuf),
|
||||
"POST %s HTTP/%s\r\n"
|
||||
"Host: %s%s\r\n"
|
||||
"User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
||||
"Content-Length: %d\r\n"
|
||||
"Content-Type: text/xml\r\n"
|
||||
"SOAPAction: \"%s\"\r\n"
|
||||
"Connection: Close\r\n"
|
||||
"Cache-Control: no-cache\r\n" /* ??? */
|
||||
"Pragma: no-cache\r\n"
|
||||
"\r\n",
|
||||
url, httpversion, host, portstr, bodysize, action);
|
||||
if ((unsigned int)headerssize >= sizeof(headerbuf))
|
||||
return -1;
|
||||
#ifdef DEBUG
|
||||
/*printf("SOAP request : headersize=%d bodysize=%d\n",
|
||||
headerssize, bodysize);
|
||||
*/
|
||||
printf("SOAP request : POST %s HTTP/%s - Host: %s%s\n",
|
||||
url, httpversion, host, portstr);
|
||||
printf("SOAPAction: \"%s\" - Content-Length: %d\n", action, bodysize);
|
||||
printf("Headers :\n%s", headerbuf);
|
||||
printf("Body :\n%s\n", body);
|
||||
#endif
|
||||
return httpWrite(fd, body, bodysize, headerbuf, headerssize);
|
||||
}
|
||||
|
||||
|
15
deps/miniupnpc/minisoap.h
vendored
Normal file
15
deps/miniupnpc/minisoap.h
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/* $Id: minisoap.h,v 1.4 2010/04/12 20:39:41 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
#ifndef MINISOAP_H_INCLUDED
|
||||
#define MINISOAP_H_INCLUDED
|
||||
|
||||
/*int httpWrite(int, const char *, int, const char *);*/
|
||||
int soapPostSubmit(int, const char *, const char *, unsigned short,
|
||||
const char *, const char *, const char *);
|
||||
|
||||
#endif
|
||||
|
876
deps/miniupnpc/minissdpc.c
vendored
Normal file
876
deps/miniupnpc/minissdpc.c
vendored
Normal file
@ -0,0 +1,876 @@
|
||||
/* $Id: minissdpc.c,v 1.32 2016/10/07 09:04:36 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2017 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
/*#include <syslog.h>*/
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#if defined (__NetBSD__)
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
#if defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#include <iphlpapi.h>
|
||||
#include <winsock.h>
|
||||
#define snprintf _snprintf
|
||||
#if !defined(_MSC_VER)
|
||||
#include <stdint.h>
|
||||
#else /* !defined(_MSC_VER) */
|
||||
typedef unsigned short uint16_t;
|
||||
#endif /* !defined(_MSC_VER) */
|
||||
#ifndef strncasecmp
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||
#define strncasecmp _memicmp
|
||||
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#define strncasecmp memicmp
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#endif /* #ifndef strncasecmp */
|
||||
#endif /* _WIN32 */
|
||||
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||
#include <sys/socket.h>
|
||||
#endif /* defined(__amigaos__) || defined(__amigaos4__) */
|
||||
#if defined(__amigaos__)
|
||||
#define uint16_t unsigned short
|
||||
#endif /* defined(__amigaos__) */
|
||||
/* Hack */
|
||||
#define UNIX_PATH_LEN 108
|
||||
struct sockaddr_un {
|
||||
uint16_t sun_family;
|
||||
char sun_path[UNIX_PATH_LEN];
|
||||
};
|
||||
#else /* defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__) */
|
||||
#include <strings.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#define closesocket close
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
||||
#else
|
||||
#define PRINT_SOCKET_ERROR(x) perror(x)
|
||||
#endif
|
||||
|
||||
#if !defined(__DragonFly__) && !defined(__OpenBSD__) && !defined(__NetBSD__) && !defined(__APPLE__) && !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__sun) && !defined(__GNU__) && !defined(__FreeBSD_kernel__)
|
||||
#define HAS_IP_MREQN
|
||||
#endif
|
||||
|
||||
#if !defined(HAS_IP_MREQN) && !defined(_WIN32)
|
||||
#include <sys/ioctl.h>
|
||||
#if defined(__sun)
|
||||
#include <sys/sockio.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(HAS_IP_MREQN) && defined(NEED_STRUCT_IP_MREQN)
|
||||
/* Several versions of glibc don't define this structure,
|
||||
* define it here and compile with CFLAGS NEED_STRUCT_IP_MREQN */
|
||||
struct ip_mreqn
|
||||
{
|
||||
struct in_addr imr_multiaddr; /* IP multicast address of group */
|
||||
struct in_addr imr_address; /* local IP address of interface */
|
||||
int imr_ifindex; /* Interface index */
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__amigaos__) || defined(__amigaos4__)
|
||||
/* Amiga OS specific stuff */
|
||||
#define TIMEVAL struct timeval
|
||||
#endif
|
||||
|
||||
#include "minissdpc.h"
|
||||
#include "miniupnpc.h"
|
||||
#include "receivedata.h"
|
||||
|
||||
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
|
||||
|
||||
#include "codelength.h"
|
||||
|
||||
struct UPNPDev *
|
||||
getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error)
|
||||
{
|
||||
struct UPNPDev * devlist = NULL;
|
||||
int s;
|
||||
int res;
|
||||
|
||||
s = connectToMiniSSDPD(socketpath);
|
||||
if (s < 0) {
|
||||
if (error)
|
||||
*error = s;
|
||||
return NULL;
|
||||
}
|
||||
res = requestDevicesFromMiniSSDPD(s, devtype);
|
||||
if (res < 0) {
|
||||
if (error)
|
||||
*error = res;
|
||||
} else {
|
||||
devlist = receiveDevicesFromMiniSSDPD(s, error);
|
||||
}
|
||||
disconnectFromMiniSSDPD(s);
|
||||
return devlist;
|
||||
}
|
||||
|
||||
/* macros used to read from unix socket */
|
||||
#define READ_BYTE_BUFFER(c) \
|
||||
if((int)bufferindex >= n) { \
|
||||
n = read(s, buffer, sizeof(buffer)); \
|
||||
if(n<=0) break; \
|
||||
bufferindex = 0; \
|
||||
} \
|
||||
c = buffer[bufferindex++];
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
#endif /* MIN */
|
||||
|
||||
#define READ_COPY_BUFFER(dst, len) \
|
||||
for(l = len, p = (unsigned char *)dst; l > 0; ) { \
|
||||
unsigned int lcopy; \
|
||||
if((int)bufferindex >= n) { \
|
||||
n = read(s, buffer, sizeof(buffer)); \
|
||||
if(n<=0) break; \
|
||||
bufferindex = 0; \
|
||||
} \
|
||||
lcopy = MIN(l, (n - bufferindex)); \
|
||||
memcpy(p, buffer + bufferindex, lcopy); \
|
||||
l -= lcopy; \
|
||||
p += lcopy; \
|
||||
bufferindex += lcopy; \
|
||||
}
|
||||
|
||||
#define READ_DISCARD_BUFFER(len) \
|
||||
for(l = len; l > 0; ) { \
|
||||
unsigned int lcopy; \
|
||||
if(bufferindex >= n) { \
|
||||
n = read(s, buffer, sizeof(buffer)); \
|
||||
if(n<=0) break; \
|
||||
bufferindex = 0; \
|
||||
} \
|
||||
lcopy = MIN(l, (n - bufferindex)); \
|
||||
l -= lcopy; \
|
||||
bufferindex += lcopy; \
|
||||
}
|
||||
|
||||
int
|
||||
connectToMiniSSDPD(const char * socketpath)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_un addr;
|
||||
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
|
||||
struct timeval timeout;
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if(s < 0)
|
||||
{
|
||||
/*syslog(LOG_ERR, "socket(unix): %m");*/
|
||||
perror("socket(unix)");
|
||||
return MINISSDPC_SOCKET_ERROR;
|
||||
}
|
||||
#if defined(MINIUPNPC_SET_SOCKET_TIMEOUT) && !defined(__sun)
|
||||
/* setting a 3 seconds timeout */
|
||||
/* not supported for AF_UNIX sockets under Solaris */
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
perror("setsockopt SO_RCVTIMEO unix");
|
||||
}
|
||||
timeout.tv_sec = 3;
|
||||
timeout.tv_usec = 0;
|
||||
if(setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(struct timeval)) < 0)
|
||||
{
|
||||
perror("setsockopt SO_SNDTIMEO unix");
|
||||
}
|
||||
#endif /* #ifdef MINIUPNPC_SET_SOCKET_TIMEOUT */
|
||||
if(!socketpath)
|
||||
socketpath = "/var/run/minissdpd.sock";
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, socketpath, sizeof(addr.sun_path));
|
||||
/* TODO : check if we need to handle the EINTR */
|
||||
if(connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)) < 0)
|
||||
{
|
||||
/*syslog(LOG_WARNING, "connect(\"%s\"): %m", socketpath);*/
|
||||
close(s);
|
||||
return MINISSDPC_SOCKET_ERROR;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
int
|
||||
disconnectFromMiniSSDPD(int s)
|
||||
{
|
||||
if (close(s) < 0)
|
||||
return MINISSDPC_SOCKET_ERROR;
|
||||
return MINISSDPC_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
requestDevicesFromMiniSSDPD(int s, const char * devtype)
|
||||
{
|
||||
unsigned char buffer[256];
|
||||
unsigned char * p;
|
||||
unsigned int stsize, l;
|
||||
|
||||
stsize = strlen(devtype);
|
||||
if(stsize == 8 && 0 == memcmp(devtype, "ssdp:all", 8))
|
||||
{
|
||||
buffer[0] = 3; /* request type 3 : everything */
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer[0] = 1; /* request type 1 : request devices/services by type */
|
||||
}
|
||||
p = buffer + 1;
|
||||
l = stsize; CODELENGTH(l, p);
|
||||
if(p + stsize > buffer + sizeof(buffer))
|
||||
{
|
||||
/* devtype is too long ! */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "devtype is too long ! stsize=%u sizeof(buffer)=%u\n",
|
||||
stsize, (unsigned)sizeof(buffer));
|
||||
#endif /* DEBUG */
|
||||
return MINISSDPC_INVALID_INPUT;
|
||||
}
|
||||
memcpy(p, devtype, stsize);
|
||||
p += stsize;
|
||||
if(write(s, buffer, p - buffer) < 0)
|
||||
{
|
||||
/*syslog(LOG_ERR, "write(): %m");*/
|
||||
perror("minissdpc.c: write()");
|
||||
return MINISSDPC_SOCKET_ERROR;
|
||||
}
|
||||
return MINISSDPC_SUCCESS;
|
||||
}
|
||||
|
||||
struct UPNPDev *
|
||||
receiveDevicesFromMiniSSDPD(int s, int * error)
|
||||
{
|
||||
struct UPNPDev * tmp;
|
||||
struct UPNPDev * devlist = NULL;
|
||||
unsigned char buffer[256];
|
||||
ssize_t n;
|
||||
unsigned char * p;
|
||||
unsigned char * url;
|
||||
unsigned char * st;
|
||||
unsigned int bufferindex;
|
||||
unsigned int i, ndev;
|
||||
unsigned int urlsize, stsize, usnsize, l;
|
||||
|
||||
n = read(s, buffer, sizeof(buffer));
|
||||
if(n<=0)
|
||||
{
|
||||
perror("minissdpc.c: read()");
|
||||
if (error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
return NULL;
|
||||
}
|
||||
ndev = buffer[0];
|
||||
bufferindex = 1;
|
||||
for(i = 0; i < ndev; i++)
|
||||
{
|
||||
DECODELENGTH_READ(urlsize, READ_BYTE_BUFFER);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
return devlist;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" urlsize=%u", urlsize);
|
||||
#endif /* DEBUG */
|
||||
url = malloc(urlsize);
|
||||
if(url == NULL) {
|
||||
if (error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
return devlist;
|
||||
}
|
||||
READ_COPY_BUFFER(url, urlsize);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
goto free_url_and_return;
|
||||
}
|
||||
DECODELENGTH_READ(stsize, READ_BYTE_BUFFER);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
goto free_url_and_return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" stsize=%u", stsize);
|
||||
#endif /* DEBUG */
|
||||
st = malloc(stsize);
|
||||
if (st == NULL) {
|
||||
if (error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
goto free_url_and_return;
|
||||
}
|
||||
READ_COPY_BUFFER(st, stsize);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
goto free_url_and_st_and_return;
|
||||
}
|
||||
DECODELENGTH_READ(usnsize, READ_BYTE_BUFFER);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
goto free_url_and_st_and_return;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf(" usnsize=%u\n", usnsize);
|
||||
#endif /* DEBUG */
|
||||
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
|
||||
if(tmp == NULL) {
|
||||
if (error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
goto free_url_and_st_and_return;
|
||||
}
|
||||
tmp->pNext = devlist;
|
||||
tmp->descURL = tmp->buffer;
|
||||
tmp->st = tmp->buffer + 1 + urlsize;
|
||||
memcpy(tmp->buffer, url, urlsize);
|
||||
tmp->buffer[urlsize] = '\0';
|
||||
memcpy(tmp->st, st, stsize);
|
||||
tmp->buffer[urlsize+1+stsize] = '\0';
|
||||
free(url);
|
||||
free(st);
|
||||
url = NULL;
|
||||
st = NULL;
|
||||
tmp->usn = tmp->buffer + 1 + urlsize + 1 + stsize;
|
||||
READ_COPY_BUFFER(tmp->usn, usnsize);
|
||||
if(n<=0) {
|
||||
if (error)
|
||||
*error = MINISSDPC_INVALID_SERVER_REPLY;
|
||||
goto free_tmp_and_return;
|
||||
}
|
||||
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
|
||||
tmp->scope_id = 0; /* default value. scope_id is not available with MiniSSDPd */
|
||||
devlist = tmp;
|
||||
}
|
||||
if (error)
|
||||
*error = MINISSDPC_SUCCESS;
|
||||
return devlist;
|
||||
|
||||
free_url_and_st_and_return:
|
||||
free(st);
|
||||
free_url_and_return:
|
||||
free(url);
|
||||
return devlist;
|
||||
|
||||
free_tmp_and_return:
|
||||
free(tmp);
|
||||
return devlist;
|
||||
}
|
||||
|
||||
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
|
||||
|
||||
/* parseMSEARCHReply()
|
||||
* the last 4 arguments are filled during the parsing :
|
||||
* - location/locationsize : "location:" field of the SSDP reply packet
|
||||
* - st/stsize : "st:" field of the SSDP reply packet.
|
||||
* The strings are NOT null terminated */
|
||||
static void
|
||||
parseMSEARCHReply(const char * reply, int size,
|
||||
const char * * location, int * locationsize,
|
||||
const char * * st, int * stsize,
|
||||
const char * * usn, int * usnsize)
|
||||
{
|
||||
int a, b, i;
|
||||
i = 0;
|
||||
a = i; /* start of the line */
|
||||
b = 0; /* end of the "header" (position of the colon) */
|
||||
while(i<size)
|
||||
{
|
||||
switch(reply[i])
|
||||
{
|
||||
case ':':
|
||||
if(b==0)
|
||||
{
|
||||
b = i; /* end of the "header" */
|
||||
/*for(j=a; j<b; j++)
|
||||
{
|
||||
putchar(reply[j]);
|
||||
}
|
||||
*/
|
||||
}
|
||||
break;
|
||||
case '\x0a':
|
||||
case '\x0d':
|
||||
if(b!=0)
|
||||
{
|
||||
/*for(j=b+1; j<i; j++)
|
||||
{
|
||||
putchar(reply[j]);
|
||||
}
|
||||
putchar('\n');*/
|
||||
/* skip the colon and white spaces */
|
||||
do { b++; } while(reply[b]==' ');
|
||||
if(0==strncasecmp(reply+a, "location", 8))
|
||||
{
|
||||
*location = reply+b;
|
||||
*locationsize = i-b;
|
||||
}
|
||||
else if(0==strncasecmp(reply+a, "st", 2))
|
||||
{
|
||||
*st = reply+b;
|
||||
*stsize = i-b;
|
||||
}
|
||||
else if(0==strncasecmp(reply+a, "usn", 3))
|
||||
{
|
||||
*usn = reply+b;
|
||||
*usnsize = i-b;
|
||||
}
|
||||
b = 0;
|
||||
}
|
||||
a = i+1;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
/* port upnp discover : SSDP protocol */
|
||||
#define SSDP_PORT 1900
|
||||
#define XSTR(s) STR(s)
|
||||
#define STR(s) #s
|
||||
#define UPNP_MCAST_ADDR "239.255.255.250"
|
||||
/* for IPv6 */
|
||||
#define UPNP_MCAST_LL_ADDR "FF02::C" /* link-local */
|
||||
#define UPNP_MCAST_SL_ADDR "FF05::C" /* site-local */
|
||||
|
||||
/* direct discovery if minissdpd responses are not sufficient */
|
||||
/* ssdpDiscoverDevices() :
|
||||
* return a chained list of all devices found or NULL if
|
||||
* no devices was found.
|
||||
* It is up to the caller to free the chained list
|
||||
* delay is in millisecond (poll).
|
||||
* UDA v1.1 says :
|
||||
* The TTL for the IP packet SHOULD default to 2 and
|
||||
* SHOULD be configurable. */
|
||||
struct UPNPDev *
|
||||
ssdpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes)
|
||||
{
|
||||
struct UPNPDev * tmp;
|
||||
struct UPNPDev * devlist = 0;
|
||||
unsigned int scope_id = 0;
|
||||
int opt = 1;
|
||||
static const char MSearchMsgFmt[] =
|
||||
"M-SEARCH * HTTP/1.1\r\n"
|
||||
"HOST: %s:" XSTR(SSDP_PORT) "\r\n"
|
||||
"ST: %s\r\n"
|
||||
"MAN: \"ssdp:discover\"\r\n"
|
||||
"MX: %u\r\n"
|
||||
"\r\n";
|
||||
int deviceIndex;
|
||||
char bufr[1536]; /* reception and emission buffer */
|
||||
int sudp;
|
||||
int n;
|
||||
struct sockaddr_storage sockudp_r;
|
||||
unsigned int mx;
|
||||
#ifdef NO_GETADDRINFO
|
||||
struct sockaddr_storage sockudp_w;
|
||||
#else
|
||||
int rv;
|
||||
struct addrinfo hints, *servinfo, *p;
|
||||
#endif
|
||||
#ifdef _WIN32
|
||||
MIB_IPFORWARDROW ip_forward;
|
||||
unsigned long _ttl = (unsigned long)ttl;
|
||||
#endif
|
||||
int linklocal = 1;
|
||||
|
||||
if(error)
|
||||
*error = MINISSDPC_UNKNOWN_ERROR;
|
||||
|
||||
if(localport==UPNP_LOCAL_PORT_SAME)
|
||||
localport = SSDP_PORT;
|
||||
|
||||
#ifdef _WIN32
|
||||
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
#else
|
||||
sudp = socket(ipv6 ? PF_INET6 : PF_INET, SOCK_DGRAM, 0);
|
||||
#endif
|
||||
if(sudp < 0)
|
||||
{
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
PRINT_SOCKET_ERROR("socket");
|
||||
return NULL;
|
||||
}
|
||||
/* reception */
|
||||
memset(&sockudp_r, 0, sizeof(struct sockaddr_storage));
|
||||
if(ipv6) {
|
||||
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_r;
|
||||
p->sin6_family = AF_INET6;
|
||||
if(localport > 0 && localport < 65536)
|
||||
p->sin6_port = htons((unsigned short)localport);
|
||||
p->sin6_addr = in6addr_any; /* in6addr_any is not available with MinGW32 3.4.2 */
|
||||
} else {
|
||||
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_r;
|
||||
p->sin_family = AF_INET;
|
||||
if(localport > 0 && localport < 65536)
|
||||
p->sin_port = htons((unsigned short)localport);
|
||||
p->sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
/* This code could help us to use the right Network interface for
|
||||
* SSDP multicast traffic */
|
||||
/* Get IP associated with the index given in the ip_forward struct
|
||||
* in order to give this ip to setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF) */
|
||||
if(!ipv6
|
||||
&& (GetBestRoute(inet_addr("223.255.255.255"), 0, &ip_forward) == NO_ERROR)) {
|
||||
DWORD dwRetVal = 0;
|
||||
PMIB_IPADDRTABLE pIPAddrTable;
|
||||
DWORD dwSize = 0;
|
||||
#ifdef DEBUG
|
||||
IN_ADDR IPAddr;
|
||||
#endif
|
||||
int i;
|
||||
#ifdef DEBUG
|
||||
printf("ifIndex=%lu nextHop=%lx \n", ip_forward.dwForwardIfIndex, ip_forward.dwForwardNextHop);
|
||||
#endif
|
||||
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(sizeof (MIB_IPADDRTABLE));
|
||||
if(pIPAddrTable) {
|
||||
if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == ERROR_INSUFFICIENT_BUFFER) {
|
||||
free(pIPAddrTable);
|
||||
pIPAddrTable = (MIB_IPADDRTABLE *) malloc(dwSize);
|
||||
}
|
||||
}
|
||||
if(pIPAddrTable) {
|
||||
dwRetVal = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
|
||||
if (dwRetVal == NO_ERROR) {
|
||||
#ifdef DEBUG
|
||||
printf("\tNum Entries: %ld\n", pIPAddrTable->dwNumEntries);
|
||||
#endif
|
||||
for (i=0; i < (int) pIPAddrTable->dwNumEntries; i++) {
|
||||
#ifdef DEBUG
|
||||
printf("\n\tInterface Index[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwIndex);
|
||||
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwAddr;
|
||||
printf("\tIP Address[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
|
||||
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwMask;
|
||||
printf("\tSubnet Mask[%d]: \t%s\n", i, inet_ntoa(IPAddr) );
|
||||
IPAddr.S_un.S_addr = (u_long) pIPAddrTable->table[i].dwBCastAddr;
|
||||
printf("\tBroadCast[%d]: \t%s (%ld)\n", i, inet_ntoa(IPAddr), pIPAddrTable->table[i].dwBCastAddr);
|
||||
printf("\tReassembly size[%d]:\t%ld\n", i, pIPAddrTable->table[i].dwReasmSize);
|
||||
printf("\tType and State[%d]:", i);
|
||||
printf("\n");
|
||||
#endif
|
||||
if (pIPAddrTable->table[i].dwIndex == ip_forward.dwForwardIfIndex) {
|
||||
/* Set the address of this interface to be used */
|
||||
struct in_addr mc_if;
|
||||
memset(&mc_if, 0, sizeof(mc_if));
|
||||
mc_if.s_addr = pIPAddrTable->table[i].dwAddr;
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0) {
|
||||
PRINT_SOCKET_ERROR("setsockopt");
|
||||
}
|
||||
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = pIPAddrTable->table[i].dwAddr;
|
||||
#ifndef DEBUG
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
free(pIPAddrTable);
|
||||
pIPAddrTable = NULL;
|
||||
}
|
||||
}
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef _WIN32
|
||||
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, (const char *)&opt, sizeof (opt)) < 0)
|
||||
#else
|
||||
if (setsockopt(sudp, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt)) < 0)
|
||||
#endif
|
||||
{
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
PRINT_SOCKET_ERROR("setsockopt(SO_REUSEADDR,...)");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, (const char *)&_ttl, sizeof(_ttl)) < 0)
|
||||
#else /* _WIN32 */
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)) < 0)
|
||||
#endif /* _WIN32 */
|
||||
{
|
||||
/* not a fatal error */
|
||||
PRINT_SOCKET_ERROR("setsockopt(IP_MULTICAST_TTL,...)");
|
||||
}
|
||||
|
||||
if(multicastif)
|
||||
{
|
||||
if(ipv6) {
|
||||
#if !defined(_WIN32)
|
||||
/* according to MSDN, if_nametoindex() is supported since
|
||||
* MS Windows Vista and MS Windows Server 2008.
|
||||
* http://msdn.microsoft.com/en-us/library/bb408409%28v=vs.85%29.aspx */
|
||||
unsigned int ifindex = if_nametoindex(multicastif); /* eth0, etc. */
|
||||
if(setsockopt(sudp, IPPROTO_IPV6, IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt IPV6_MULTICAST_IF");
|
||||
}
|
||||
#else
|
||||
#ifdef DEBUG
|
||||
printf("Setting of multicast interface not supported in IPv6 under Windows.\n");
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
struct in_addr mc_if;
|
||||
mc_if.s_addr = inet_addr(multicastif); /* ex: 192.168.x.x */
|
||||
if(mc_if.s_addr != INADDR_NONE)
|
||||
{
|
||||
((struct sockaddr_in *)&sockudp_r)->sin_addr.s_addr = mc_if.s_addr;
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||
}
|
||||
} else {
|
||||
#ifdef HAS_IP_MREQN
|
||||
/* was not an ip address, try with an interface name */
|
||||
struct ip_mreqn reqn; /* only defined with -D_BSD_SOURCE or -D_GNU_SOURCE */
|
||||
memset(&reqn, 0, sizeof(struct ip_mreqn));
|
||||
reqn.imr_ifindex = if_nametoindex(multicastif);
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&reqn, sizeof(reqn)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||
}
|
||||
#elif !defined(_WIN32)
|
||||
struct ifreq ifr;
|
||||
int ifrlen = sizeof(ifr);
|
||||
strncpy(ifr.ifr_name, multicastif, IFNAMSIZ);
|
||||
ifr.ifr_name[IFNAMSIZ-1] = '\0';
|
||||
if(ioctl(sudp, SIOCGIFADDR, &ifr, &ifrlen) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("ioctl(...SIOCGIFADDR...)");
|
||||
}
|
||||
mc_if.s_addr = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr;
|
||||
if(setsockopt(sudp, IPPROTO_IP, IP_MULTICAST_IF, (const char *)&mc_if, sizeof(mc_if)) < 0)
|
||||
{
|
||||
PRINT_SOCKET_ERROR("setsockopt IP_MULTICAST_IF");
|
||||
}
|
||||
#else /* _WIN32 */
|
||||
#ifdef DEBUG
|
||||
printf("Setting of multicast interface not supported with interface name.\n");
|
||||
#endif
|
||||
#endif /* #ifdef HAS_IP_MREQN / !defined(_WIN32) */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Before sending the packed, we first "bind" in order to be able
|
||||
* to receive the response */
|
||||
if (bind(sudp, (const struct sockaddr *)&sockudp_r,
|
||||
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)) != 0)
|
||||
{
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
PRINT_SOCKET_ERROR("bind");
|
||||
closesocket(sudp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(error)
|
||||
*error = MINISSDPC_SUCCESS;
|
||||
/* Calculating maximum response time in seconds */
|
||||
mx = ((unsigned int)delay) / 1000u;
|
||||
if(mx == 0) {
|
||||
mx = 1;
|
||||
delay = 1000;
|
||||
}
|
||||
/* receiving SSDP response packet */
|
||||
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
|
||||
/* sending the SSDP M-SEARCH packet */
|
||||
n = snprintf(bufr, sizeof(bufr),
|
||||
MSearchMsgFmt,
|
||||
ipv6 ?
|
||||
(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
|
||||
: UPNP_MCAST_ADDR,
|
||||
deviceTypes[deviceIndex], mx);
|
||||
if ((unsigned int)n >= sizeof(bufr)) {
|
||||
if(error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
goto error;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
/*printf("Sending %s", bufr);*/
|
||||
printf("Sending M-SEARCH request to %s with ST: %s\n",
|
||||
ipv6 ?
|
||||
(linklocal ? "[" UPNP_MCAST_LL_ADDR "]" : "[" UPNP_MCAST_SL_ADDR "]")
|
||||
: UPNP_MCAST_ADDR,
|
||||
deviceTypes[deviceIndex]);
|
||||
#endif
|
||||
#ifdef NO_GETADDRINFO
|
||||
/* the following code is not using getaddrinfo */
|
||||
/* emission */
|
||||
memset(&sockudp_w, 0, sizeof(struct sockaddr_storage));
|
||||
if(ipv6) {
|
||||
struct sockaddr_in6 * p = (struct sockaddr_in6 *)&sockudp_w;
|
||||
p->sin6_family = AF_INET6;
|
||||
p->sin6_port = htons(SSDP_PORT);
|
||||
inet_pton(AF_INET6,
|
||||
linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR,
|
||||
&(p->sin6_addr));
|
||||
} else {
|
||||
struct sockaddr_in * p = (struct sockaddr_in *)&sockudp_w;
|
||||
p->sin_family = AF_INET;
|
||||
p->sin_port = htons(SSDP_PORT);
|
||||
p->sin_addr.s_addr = inet_addr(UPNP_MCAST_ADDR);
|
||||
}
|
||||
n = sendto(sudp, bufr, n, 0, &sockudp_w,
|
||||
ipv6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
|
||||
if (n < 0) {
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
PRINT_SOCKET_ERROR("sendto");
|
||||
break;
|
||||
}
|
||||
#else /* #ifdef NO_GETADDRINFO */
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_UNSPEC; /* AF_INET6 or AF_INET */
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
/*hints.ai_flags = */
|
||||
if ((rv = getaddrinfo(ipv6
|
||||
? (linklocal ? UPNP_MCAST_LL_ADDR : UPNP_MCAST_SL_ADDR)
|
||||
: UPNP_MCAST_ADDR,
|
||||
XSTR(SSDP_PORT), &hints, &servinfo)) != 0) {
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
#ifdef _WIN32
|
||||
fprintf(stderr, "getaddrinfo() failed: %d\n", rv);
|
||||
#else
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
for(p = servinfo; p; p = p->ai_next) {
|
||||
n = sendto(sudp, bufr, n, 0, p->ai_addr, p->ai_addrlen);
|
||||
if (n < 0) {
|
||||
#ifdef DEBUG
|
||||
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
|
||||
if (getnameinfo(p->ai_addr, p->ai_addrlen, hbuf, sizeof(hbuf), sbuf,
|
||||
sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||
fprintf(stderr, "host:%s port:%s\n", hbuf, sbuf);
|
||||
}
|
||||
#endif
|
||||
PRINT_SOCKET_ERROR("sendto");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
freeaddrinfo(servinfo);
|
||||
if(n < 0) {
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
break;
|
||||
}
|
||||
#endif /* #ifdef NO_GETADDRINFO */
|
||||
/* Waiting for SSDP REPLY packet to M-SEARCH
|
||||
* if searchalltypes is set, enter the loop only
|
||||
* when the last deviceType is reached */
|
||||
if(!searchalltypes || !deviceTypes[deviceIndex + 1]) do {
|
||||
n = receivedata(sudp, bufr, sizeof(bufr), delay, &scope_id);
|
||||
if (n < 0) {
|
||||
/* error */
|
||||
if(error)
|
||||
*error = MINISSDPC_SOCKET_ERROR;
|
||||
goto error;
|
||||
} else if (n == 0) {
|
||||
/* no data or Time Out */
|
||||
#ifdef DEBUG
|
||||
printf("NODATA or TIMEOUT\n");
|
||||
#endif /* DEBUG */
|
||||
if (devlist && !searchalltypes) {
|
||||
/* found some devices, stop now*/
|
||||
if(error)
|
||||
*error = MINISSDPC_SUCCESS;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
const char * descURL=NULL;
|
||||
int urlsize=0;
|
||||
const char * st=NULL;
|
||||
int stsize=0;
|
||||
const char * usn=NULL;
|
||||
int usnsize=0;
|
||||
parseMSEARCHReply(bufr, n, &descURL, &urlsize, &st, &stsize, &usn, &usnsize);
|
||||
if(st&&descURL) {
|
||||
#ifdef DEBUG
|
||||
printf("M-SEARCH Reply:\n ST: %.*s\n USN: %.*s\n Location: %.*s\n",
|
||||
stsize, st, usnsize, (usn?usn:""), urlsize, descURL);
|
||||
#endif /* DEBUG */
|
||||
for(tmp=devlist; tmp; tmp = tmp->pNext) {
|
||||
if(memcmp(tmp->descURL, descURL, urlsize) == 0 &&
|
||||
tmp->descURL[urlsize] == '\0' &&
|
||||
memcmp(tmp->st, st, stsize) == 0 &&
|
||||
tmp->st[stsize] == '\0' &&
|
||||
(usnsize == 0 || memcmp(tmp->usn, usn, usnsize) == 0) &&
|
||||
tmp->usn[usnsize] == '\0')
|
||||
break;
|
||||
}
|
||||
/* at the exit of the loop above, tmp is null if
|
||||
* no duplicate device was found */
|
||||
if(tmp)
|
||||
continue;
|
||||
tmp = (struct UPNPDev *)malloc(sizeof(struct UPNPDev)+urlsize+stsize+usnsize);
|
||||
if(!tmp) {
|
||||
/* memory allocation error */
|
||||
if(error)
|
||||
*error = MINISSDPC_MEMORY_ERROR;
|
||||
goto error;
|
||||
}
|
||||
tmp->pNext = devlist;
|
||||
tmp->descURL = tmp->buffer;
|
||||
tmp->st = tmp->buffer + 1 + urlsize;
|
||||
tmp->usn = tmp->st + 1 + stsize;
|
||||
memcpy(tmp->buffer, descURL, urlsize);
|
||||
tmp->buffer[urlsize] = '\0';
|
||||
memcpy(tmp->st, st, stsize);
|
||||
tmp->buffer[urlsize+1+stsize] = '\0';
|
||||
if(usn != NULL)
|
||||
memcpy(tmp->usn, usn, usnsize);
|
||||
tmp->buffer[urlsize+1+stsize+1+usnsize] = '\0';
|
||||
tmp->scope_id = scope_id;
|
||||
devlist = tmp;
|
||||
}
|
||||
}
|
||||
} while(n > 0);
|
||||
if(ipv6) {
|
||||
/* switch linklocal flag */
|
||||
if(linklocal) {
|
||||
linklocal = 0;
|
||||
--deviceIndex;
|
||||
} else {
|
||||
linklocal = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
error:
|
||||
closesocket(sudp);
|
||||
return devlist;
|
||||
}
|
||||
|
58
deps/miniupnpc/minissdpc.h
vendored
Normal file
58
deps/miniupnpc/minissdpc.h
vendored
Normal file
@ -0,0 +1,58 @@
|
||||
/* $Id: minissdpc.h,v 1.6 2015/09/18 12:45:16 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINISSDPC_H_INCLUDED
|
||||
#define MINISSDPC_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
#include "upnpdev.h"
|
||||
|
||||
/* error codes : */
|
||||
#define MINISSDPC_SUCCESS (0)
|
||||
#define MINISSDPC_UNKNOWN_ERROR (-1)
|
||||
#define MINISSDPC_SOCKET_ERROR (-101)
|
||||
#define MINISSDPC_MEMORY_ERROR (-102)
|
||||
#define MINISSDPC_INVALID_INPUT (-103)
|
||||
#define MINISSDPC_INVALID_SERVER_REPLY (-104)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__))
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
getDevicesFromMiniSSDPD(const char * devtype, const char * socketpath, int * error);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
connectToMiniSSDPD(const char * socketpath);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
disconnectFromMiniSSDPD(int fd);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
requestDevicesFromMiniSSDPD(int fd, const char * devtype);
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
receiveDevicesFromMiniSSDPD(int fd, int * error);
|
||||
|
||||
#endif /* !(defined(_WIN32) || defined(__amigaos__) || defined(__amigaos4__)) */
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
ssdpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
722
deps/miniupnpc/miniupnpc.c
vendored
Normal file
722
deps/miniupnpc/miniupnpc.c
vendored
Normal file
@ -0,0 +1,722 @@
|
||||
/* $Id: miniupnpc.c,v 1.148 2016/01/24 17:24:36 nanard Exp $ */
|
||||
/* vim: tabstop=4 shiftwidth=4 noexpandtab
|
||||
* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2016 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
/* Win32 Specific includes and defines */
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#include <iphlpapi.h>
|
||||
#define snprintf _snprintf
|
||||
#define strdup _strdup
|
||||
#ifndef strncasecmp
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||
#define strncasecmp _memicmp
|
||||
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#define strncasecmp memicmp
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#endif /* #ifndef strncasecmp */
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#else /* #ifdef _WIN32 */
|
||||
/* Standard POSIX includes */
|
||||
#include <unistd.h>
|
||||
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* Amiga OS 3 specific stuff */
|
||||
#define socklen_t int
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/param.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <net/if.h>
|
||||
#if !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
#include <poll.h>
|
||||
#endif
|
||||
#include <strings.h>
|
||||
#include <errno.h>
|
||||
#define closesocket close
|
||||
#endif /* #else _WIN32 */
|
||||
#ifdef __GNU__
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
|
||||
#include "miniupnpc.h"
|
||||
#include "minissdpc.h"
|
||||
#include "miniwget.h"
|
||||
#include "minisoap.h"
|
||||
#include "minixml.h"
|
||||
#include "upnpcommands.h"
|
||||
#include "connecthostport.h"
|
||||
|
||||
/* compare the begining of a string with a constant string */
|
||||
#define COMPARE(str, cstr) (0==memcmp(str, cstr, sizeof(cstr) - 1))
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
#define SOAPPREFIX "s"
|
||||
#define SERVICEPREFIX "u"
|
||||
#define SERVICEPREFIX2 'u'
|
||||
|
||||
/* check if an ip address is a private (LAN) address
|
||||
* see https://tools.ietf.org/html/rfc1918 */
|
||||
static int is_rfc1918addr(const char * addr)
|
||||
{
|
||||
/* 192.168.0.0 - 192.168.255.255 (192.168/16 prefix) */
|
||||
if(COMPARE(addr, "192.168."))
|
||||
return 1;
|
||||
/* 10.0.0.0 - 10.255.255.255 (10/8 prefix) */
|
||||
if(COMPARE(addr, "10."))
|
||||
return 1;
|
||||
/* 172.16.0.0 - 172.31.255.255 (172.16/12 prefix) */
|
||||
if(COMPARE(addr, "172.")) {
|
||||
int i = atoi(addr + 4);
|
||||
if((16 <= i) && (i <= 31))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* root description parsing */
|
||||
MINIUPNP_LIBSPEC void parserootdesc(const char * buffer, int bufsize, struct IGDdatas * data)
|
||||
{
|
||||
struct xmlparser parser;
|
||||
/* xmlparser object */
|
||||
parser.xmlstart = buffer;
|
||||
parser.xmlsize = bufsize;
|
||||
parser.data = data;
|
||||
parser.starteltfunc = IGDstartelt;
|
||||
parser.endeltfunc = IGDendelt;
|
||||
parser.datafunc = IGDdata;
|
||||
parser.attfunc = 0;
|
||||
parsexml(&parser);
|
||||
#ifdef DEBUG
|
||||
printIGD(data);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* simpleUPnPcommand2 :
|
||||
* not so simple !
|
||||
* return values :
|
||||
* pointer - OK
|
||||
* NULL - error */
|
||||
char * simpleUPnPcommand2(int s, const char * url, const char * service,
|
||||
const char * action, struct UPNParg * args,
|
||||
int * bufsize, const char * httpversion)
|
||||
{
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
unsigned short port = 0;
|
||||
char * path;
|
||||
char soapact[128];
|
||||
char soapbody[2048];
|
||||
int soapbodylen;
|
||||
char * buf;
|
||||
int n;
|
||||
int status_code;
|
||||
|
||||
*bufsize = 0;
|
||||
snprintf(soapact, sizeof(soapact), "%s#%s", service, action);
|
||||
if(args==NULL)
|
||||
{
|
||||
soapbodylen = snprintf(soapbody, sizeof(soapbody),
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<" SOAPPREFIX ":Envelope "
|
||||
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<" SOAPPREFIX ":Body>"
|
||||
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">"
|
||||
"</" SERVICEPREFIX ":%s>"
|
||||
"</" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>"
|
||||
"\r\n", action, service, action);
|
||||
if ((unsigned int)soapbodylen >= sizeof(soapbody))
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
char * p;
|
||||
const char * pe, * pv;
|
||||
const char * const pend = soapbody + sizeof(soapbody);
|
||||
soapbodylen = snprintf(soapbody, sizeof(soapbody),
|
||||
"<?xml version=\"1.0\"?>\r\n"
|
||||
"<" SOAPPREFIX ":Envelope "
|
||||
"xmlns:" SOAPPREFIX "=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
||||
SOAPPREFIX ":encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
||||
"<" SOAPPREFIX ":Body>"
|
||||
"<" SERVICEPREFIX ":%s xmlns:" SERVICEPREFIX "=\"%s\">",
|
||||
action, service);
|
||||
if ((unsigned int)soapbodylen >= sizeof(soapbody))
|
||||
return NULL;
|
||||
p = soapbody + soapbodylen;
|
||||
while(args->elt)
|
||||
{
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
|
||||
pe = args->elt;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '>';
|
||||
|
||||
if((pv = args->val))
|
||||
{
|
||||
while(p < pend && *pv)
|
||||
*(p++) = *(pv++);
|
||||
}
|
||||
|
||||
if((p+2) > pend) /* check for space to write next 2 bytes */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
*(p++) = '/';
|
||||
|
||||
pe = args->elt;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
if(p >= pend) /* check for space to write next byte */
|
||||
return NULL;
|
||||
*(p++) = '>';
|
||||
|
||||
args++;
|
||||
}
|
||||
if((p+4) > pend) /* check for space to write next 4 bytes */
|
||||
return NULL;
|
||||
*(p++) = '<';
|
||||
*(p++) = '/';
|
||||
*(p++) = SERVICEPREFIX2;
|
||||
*(p++) = ':';
|
||||
|
||||
pe = action;
|
||||
while(p < pend && *pe)
|
||||
*(p++) = *(pe++);
|
||||
|
||||
strncpy(p, "></" SOAPPREFIX ":Body></" SOAPPREFIX ":Envelope>\r\n",
|
||||
pend - p);
|
||||
if(soapbody[sizeof(soapbody)-1]) /* strncpy pads buffer with 0s, so if it doesn't end in 0, could not fit full string */
|
||||
return NULL;
|
||||
}
|
||||
if(!parseURL(url, hostname, &port, &path, NULL)) return NULL;
|
||||
if(s < 0) {
|
||||
s = connecthostport(hostname, port, 0);
|
||||
if(s < 0) {
|
||||
/* failed to connect */
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
n = soapPostSubmit(s, path, hostname, port, soapact, soapbody, httpversion);
|
||||
if(n<=0) {
|
||||
#ifdef DEBUG
|
||||
printf("Error sending SOAP request\n");
|
||||
#endif
|
||||
closesocket(s);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buf = getHTTPResponse(s, bufsize, &status_code);
|
||||
#ifdef DEBUG
|
||||
if(*bufsize > 0 && buf)
|
||||
{
|
||||
printf("HTTP %d SOAP Response :\n%.*s\n", status_code, *bufsize, buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("HTTP %d, empty SOAP response. size=%d\n", status_code, *bufsize);
|
||||
}
|
||||
#endif
|
||||
closesocket(s);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* simpleUPnPcommand :
|
||||
* not so simple !
|
||||
* return values :
|
||||
* pointer - OK
|
||||
* NULL - error */
|
||||
char * simpleUPnPcommand(int s, const char * url, const char * service,
|
||||
const char * action, struct UPNParg * args,
|
||||
int * bufsize)
|
||||
{
|
||||
char * buf;
|
||||
|
||||
#if 1
|
||||
buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
|
||||
#else
|
||||
buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.0");
|
||||
if (!buf || *bufsize == 0)
|
||||
{
|
||||
#if DEBUG
|
||||
printf("Error or no result from SOAP request; retrying with HTTP/1.1\n");
|
||||
#endif
|
||||
buf = simpleUPnPcommand2(s, url, service, action, args, bufsize, "1.1");
|
||||
}
|
||||
#endif
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* upnpDiscoverDevices() :
|
||||
* return a chained list of all devices found or NULL if
|
||||
* no devices was found.
|
||||
* It is up to the caller to free the chained list
|
||||
* delay is in millisecond (poll).
|
||||
* UDA v1.1 says :
|
||||
* The TTL for the IP packet SHOULD default to 2 and
|
||||
* SHOULD be configurable. */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes)
|
||||
{
|
||||
struct UPNPDev * tmp;
|
||||
struct UPNPDev * devlist = 0;
|
||||
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
int deviceIndex;
|
||||
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
|
||||
if(error)
|
||||
*error = UPNPDISCOVER_UNKNOWN_ERROR;
|
||||
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* first try to get infos from minissdpd ! */
|
||||
if(!minissdpdsock)
|
||||
minissdpdsock = "/var/run/minissdpd.sock";
|
||||
for(deviceIndex = 0; deviceTypes[deviceIndex]; deviceIndex++) {
|
||||
struct UPNPDev * minissdpd_devlist;
|
||||
int only_rootdevice = 1;
|
||||
minissdpd_devlist = getDevicesFromMiniSSDPD(deviceTypes[deviceIndex],
|
||||
minissdpdsock, 0);
|
||||
if(minissdpd_devlist) {
|
||||
#ifdef DEBUG
|
||||
printf("returned by MiniSSDPD: %s\t%s\n",
|
||||
minissdpd_devlist->st, minissdpd_devlist->descURL);
|
||||
#endif /* DEBUG */
|
||||
if(!strstr(minissdpd_devlist->st, "rootdevice"))
|
||||
only_rootdevice = 0;
|
||||
for(tmp = minissdpd_devlist; tmp->pNext != NULL; tmp = tmp->pNext) {
|
||||
#ifdef DEBUG
|
||||
printf("returned by MiniSSDPD: %s\t%s\n",
|
||||
tmp->pNext->st, tmp->pNext->descURL);
|
||||
#endif /* DEBUG */
|
||||
if(!strstr(tmp->st, "rootdevice"))
|
||||
only_rootdevice = 0;
|
||||
}
|
||||
tmp->pNext = devlist;
|
||||
devlist = minissdpd_devlist;
|
||||
if(!searchalltypes && !only_rootdevice)
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(tmp = devlist; tmp != NULL; tmp = tmp->pNext) {
|
||||
/* We return what we have found if it was not only a rootdevice */
|
||||
if(!strstr(tmp->st, "rootdevice")) {
|
||||
if(error)
|
||||
*error = UPNPDISCOVER_SUCCESS;
|
||||
return devlist;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
|
||||
/* direct discovery if minissdpd responses are not sufficient */
|
||||
{
|
||||
struct UPNPDev * discovered_devlist;
|
||||
discovered_devlist = ssdpDiscoverDevices(deviceTypes, delay, multicastif, localport,
|
||||
ipv6, ttl, error, searchalltypes);
|
||||
if(devlist == NULL)
|
||||
devlist = discovered_devlist;
|
||||
else {
|
||||
for(tmp = devlist; tmp->pNext != NULL; tmp = tmp->pNext);
|
||||
tmp->pNext = discovered_devlist;
|
||||
}
|
||||
}
|
||||
return devlist;
|
||||
}
|
||||
|
||||
/* upnpDiscover() Discover IGD device */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
static const char * const deviceList[] = {
|
||||
#if 0
|
||||
"urn:schemas-upnp-org:device:InternetGatewayDevice:2",
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:2",
|
||||
#endif
|
||||
"urn:schemas-upnp-org:device:InternetGatewayDevice:1",
|
||||
"urn:schemas-upnp-org:service:WANIPConnection:1",
|
||||
"urn:schemas-upnp-org:service:WANPPPConnection:1",
|
||||
"upnp:rootdevice",
|
||||
/*"ssdp:all",*/
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
/* upnpDiscoverAll() Discover all UPnP devices */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverAll(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
static const char * const deviceList[] = {
|
||||
/*"upnp:rootdevice",*/
|
||||
"ssdp:all",
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
/* upnpDiscoverDevice() Discover a specific device */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error)
|
||||
{
|
||||
const char * const deviceList[] = {
|
||||
device,
|
||||
0
|
||||
};
|
||||
return upnpDiscoverDevices(deviceList,
|
||||
delay, multicastif, minissdpdsock, localport,
|
||||
ipv6, ttl, error, 0);
|
||||
}
|
||||
|
||||
static char *
|
||||
build_absolute_url(const char * baseurl, const char * descURL,
|
||||
const char * url, unsigned int scope_id)
|
||||
{
|
||||
int l, n;
|
||||
char * s;
|
||||
const char * base;
|
||||
char * p;
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
char ifname[IF_NAMESIZE];
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
char scope_str[8];
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
|
||||
if( (url[0] == 'h')
|
||||
&&(url[1] == 't')
|
||||
&&(url[2] == 't')
|
||||
&&(url[3] == 'p')
|
||||
&&(url[4] == ':')
|
||||
&&(url[5] == '/')
|
||||
&&(url[6] == '/'))
|
||||
return strdup(url);
|
||||
base = (baseurl[0] == '\0') ? descURL : baseurl;
|
||||
n = strlen(base);
|
||||
if(n > 7) {
|
||||
p = strchr(base + 7, '/');
|
||||
if(p)
|
||||
n = p - base;
|
||||
}
|
||||
l = n + strlen(url) + 1;
|
||||
if(url[0] != '/')
|
||||
l++;
|
||||
if(scope_id != 0) {
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
if(if_indextoname(scope_id, ifname)) {
|
||||
l += 3 + strlen(ifname); /* 3 == strlen(%25) */
|
||||
}
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
/* under windows, scope is numerical */
|
||||
l += 3 + snprintf(scope_str, sizeof(scope_str), "%u", scope_id);
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
}
|
||||
s = malloc(l);
|
||||
if(s == NULL) return NULL;
|
||||
memcpy(s, base, n);
|
||||
if(scope_id != 0) {
|
||||
s[n] = '\0';
|
||||
if(0 == memcmp(s, "http://[fe80:", 13)) {
|
||||
/* this is a linklocal IPv6 address */
|
||||
p = strchr(s, ']');
|
||||
if(p) {
|
||||
/* insert %25<scope> into URL */
|
||||
#if defined(IF_NAMESIZE) && !defined(_WIN32)
|
||||
memmove(p + 3 + strlen(ifname), p, strlen(p) + 1);
|
||||
memcpy(p, "%25", 3);
|
||||
memcpy(p + 3, ifname, strlen(ifname));
|
||||
n += 3 + strlen(ifname);
|
||||
#else /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
memmove(p + 3 + strlen(scope_str), p, strlen(p) + 1);
|
||||
memcpy(p, "%25", 3);
|
||||
memcpy(p + 3, scope_str, strlen(scope_str));
|
||||
n += 3 + strlen(scope_str);
|
||||
#endif /* defined(IF_NAMESIZE) && !defined(_WIN32) */
|
||||
}
|
||||
}
|
||||
}
|
||||
if(url[0] != '/')
|
||||
s[n++] = '/';
|
||||
memcpy(s + n, url, l - n);
|
||||
return s;
|
||||
}
|
||||
|
||||
/* Prepare the Urls for usage...
|
||||
*/
|
||||
MINIUPNP_LIBSPEC void
|
||||
GetUPNPUrls(struct UPNPUrls * urls, struct IGDdatas * data,
|
||||
const char * descURL, unsigned int scope_id)
|
||||
{
|
||||
/* strdup descURL */
|
||||
urls->rootdescURL = strdup(descURL);
|
||||
|
||||
/* get description of WANIPConnection */
|
||||
urls->ipcondescURL = build_absolute_url(data->urlbase, descURL,
|
||||
data->first.scpdurl, scope_id);
|
||||
urls->controlURL = build_absolute_url(data->urlbase, descURL,
|
||||
data->first.controlurl, scope_id);
|
||||
urls->controlURL_CIF = build_absolute_url(data->urlbase, descURL,
|
||||
data->CIF.controlurl, scope_id);
|
||||
urls->controlURL_6FC = build_absolute_url(data->urlbase, descURL,
|
||||
data->IPv6FC.controlurl, scope_id);
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("urls->ipcondescURL='%s'\n", urls->ipcondescURL);
|
||||
printf("urls->controlURL='%s'\n", urls->controlURL);
|
||||
printf("urls->controlURL_CIF='%s'\n", urls->controlURL_CIF);
|
||||
printf("urls->controlURL_6FC='%s'\n", urls->controlURL_6FC);
|
||||
#endif
|
||||
}
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
FreeUPNPUrls(struct UPNPUrls * urls)
|
||||
{
|
||||
if(!urls)
|
||||
return;
|
||||
free(urls->controlURL);
|
||||
urls->controlURL = 0;
|
||||
free(urls->ipcondescURL);
|
||||
urls->ipcondescURL = 0;
|
||||
free(urls->controlURL_CIF);
|
||||
urls->controlURL_CIF = 0;
|
||||
free(urls->controlURL_6FC);
|
||||
urls->controlURL_6FC = 0;
|
||||
free(urls->rootdescURL);
|
||||
urls->rootdescURL = 0;
|
||||
}
|
||||
|
||||
int
|
||||
UPNPIGD_IsConnected(struct UPNPUrls * urls, struct IGDdatas * data)
|
||||
{
|
||||
char status[64];
|
||||
unsigned int uptime;
|
||||
status[0] = '\0';
|
||||
UPNP_GetStatusInfo(urls->controlURL, data->first.servicetype,
|
||||
status, &uptime, NULL);
|
||||
if(0 == strcmp("Connected", status))
|
||||
return 1;
|
||||
else if(0 == strcmp("Up", status)) /* Also accept "Up" */
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* UPNP_GetValidIGD() :
|
||||
* return values :
|
||||
* -1 = Internal error
|
||||
* 0 = NO IGD found
|
||||
* 1 = A valid connected IGD has been found
|
||||
* 2 = A valid IGD has been found but it reported as
|
||||
* not connected
|
||||
* 3 = an UPnP device has been found but was not recognized as an IGD
|
||||
*
|
||||
* In any positive non zero return case, the urls and data structures
|
||||
* passed as parameters are set. Dont forget to call FreeUPNPUrls(urls) to
|
||||
* free allocated memory.
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen)
|
||||
{
|
||||
struct xml_desc {
|
||||
char * xml;
|
||||
int size;
|
||||
int is_igd;
|
||||
} * desc = NULL;
|
||||
struct UPNPDev * dev;
|
||||
int ndev = 0;
|
||||
int i;
|
||||
int state = -1; /* state 1 : IGD connected. State 2 : IGD. State 3 : anything */
|
||||
int n_igd = 0;
|
||||
char extIpAddr[16];
|
||||
char myLanAddr[40];
|
||||
int status_code = -1;
|
||||
|
||||
if(!devlist)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Empty devlist\n");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
/* counting total number of devices in the list */
|
||||
for(dev = devlist; dev; dev = dev->pNext)
|
||||
ndev++;
|
||||
if(ndev > 0)
|
||||
{
|
||||
desc = calloc(ndev, sizeof(struct xml_desc));
|
||||
if(!desc)
|
||||
return -1; /* memory allocation error */
|
||||
}
|
||||
/* Step 1 : downloading descriptions and testing type */
|
||||
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
|
||||
{
|
||||
/* we should choose an internet gateway device.
|
||||
* with st == urn:schemas-upnp-org:device:InternetGatewayDevice:1 */
|
||||
desc[i].xml = miniwget_getaddr(dev->descURL, &(desc[i].size),
|
||||
myLanAddr, sizeof(myLanAddr),
|
||||
dev->scope_id, &status_code);
|
||||
#ifdef DEBUG
|
||||
if(!desc[i].xml)
|
||||
{
|
||||
printf("error getting XML description %s\n", dev->descURL);
|
||||
}
|
||||
#endif
|
||||
if(desc[i].xml)
|
||||
{
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(desc[i].xml, desc[i].size, data);
|
||||
if(COMPARE(data->CIF.servicetype,
|
||||
"urn:schemas-upnp-org:service:WANCommonInterfaceConfig:"))
|
||||
{
|
||||
desc[i].is_igd = 1;
|
||||
n_igd++;
|
||||
if(lanaddr)
|
||||
strncpy(lanaddr, myLanAddr, lanaddrlen);
|
||||
}
|
||||
}
|
||||
}
|
||||
/* iterate the list to find a device depending on state */
|
||||
for(state = 1; state <= 3; state++)
|
||||
{
|
||||
for(dev = devlist, i = 0; dev; dev = dev->pNext, i++)
|
||||
{
|
||||
if(desc[i].xml)
|
||||
{
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(desc[i].xml, desc[i].size, data);
|
||||
if(desc[i].is_igd || state >= 3 )
|
||||
{
|
||||
int is_connected;
|
||||
|
||||
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
|
||||
|
||||
/* in state 2 and 3 we dont test if device is connected ! */
|
||||
if(state >= 2)
|
||||
goto free_and_return;
|
||||
is_connected = UPNPIGD_IsConnected(urls, data);
|
||||
#ifdef DEBUG
|
||||
printf("UPNPIGD_IsConnected(%s) = %d\n",
|
||||
urls->controlURL, is_connected);
|
||||
#endif
|
||||
/* checks that status is connected AND there is a external IP address assigned */
|
||||
if(is_connected &&
|
||||
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
|
||||
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
|
||||
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
|
||||
goto free_and_return;
|
||||
}
|
||||
FreeUPNPUrls(urls);
|
||||
if(data->second.servicetype[0] != '\0') {
|
||||
#ifdef DEBUG
|
||||
printf("We tried %s, now we try %s !\n",
|
||||
data->first.servicetype, data->second.servicetype);
|
||||
#endif
|
||||
/* swaping WANPPPConnection and WANIPConnection ! */
|
||||
memcpy(&data->tmp, &data->first, sizeof(struct IGDdatas_service));
|
||||
memcpy(&data->first, &data->second, sizeof(struct IGDdatas_service));
|
||||
memcpy(&data->second, &data->tmp, sizeof(struct IGDdatas_service));
|
||||
GetUPNPUrls(urls, data, dev->descURL, dev->scope_id);
|
||||
is_connected = UPNPIGD_IsConnected(urls, data);
|
||||
#ifdef DEBUG
|
||||
printf("UPNPIGD_IsConnected(%s) = %d\n",
|
||||
urls->controlURL, is_connected);
|
||||
#endif
|
||||
if(is_connected &&
|
||||
(UPNP_GetExternalIPAddress(urls->controlURL, data->first.servicetype, extIpAddr) == 0)) {
|
||||
if(!is_rfc1918addr(extIpAddr) && (extIpAddr[0] != '\0')
|
||||
&& (0 != strcmp(extIpAddr, "0.0.0.0")))
|
||||
goto free_and_return;
|
||||
}
|
||||
FreeUPNPUrls(urls);
|
||||
}
|
||||
}
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
}
|
||||
}
|
||||
}
|
||||
state = 0;
|
||||
free_and_return:
|
||||
if(desc) {
|
||||
for(i = 0; i < ndev; i++) {
|
||||
if(desc[i].xml) {
|
||||
free(desc[i].xml);
|
||||
}
|
||||
}
|
||||
free(desc);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/* UPNP_GetIGDFromUrl()
|
||||
* Used when skipping the discovery process.
|
||||
* return value :
|
||||
* 0 - Not ok
|
||||
* 1 - OK */
|
||||
int
|
||||
UPNP_GetIGDFromUrl(const char * rootdescurl,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen)
|
||||
{
|
||||
char * descXML;
|
||||
int descXMLsize = 0;
|
||||
|
||||
descXML = miniwget_getaddr(rootdescurl, &descXMLsize,
|
||||
lanaddr, lanaddrlen, 0, NULL);
|
||||
if(descXML) {
|
||||
memset(data, 0, sizeof(struct IGDdatas));
|
||||
memset(urls, 0, sizeof(struct UPNPUrls));
|
||||
parserootdesc(descXML, descXMLsize, data);
|
||||
free(descXML);
|
||||
descXML = NULL;
|
||||
GetUPNPUrls(urls, data, rootdescurl, 0);
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
45
deps/miniupnpc/miniupnpc.def
vendored
Normal file
45
deps/miniupnpc/miniupnpc.def
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
LIBRARY
|
||||
; miniupnpc library
|
||||
miniupnpc
|
||||
|
||||
EXPORTS
|
||||
; miniupnpc
|
||||
upnpDiscover
|
||||
freeUPNPDevlist
|
||||
parserootdesc
|
||||
UPNP_GetValidIGD
|
||||
UPNP_GetIGDFromUrl
|
||||
GetUPNPUrls
|
||||
FreeUPNPUrls
|
||||
; miniwget
|
||||
miniwget
|
||||
miniwget_getaddr
|
||||
; upnpcommands
|
||||
UPNP_GetTotalBytesSent
|
||||
UPNP_GetTotalBytesReceived
|
||||
UPNP_GetTotalPacketsSent
|
||||
UPNP_GetTotalPacketsReceived
|
||||
UPNP_GetStatusInfo
|
||||
UPNP_GetConnectionTypeInfo
|
||||
UPNP_GetExternalIPAddress
|
||||
UPNP_GetLinkLayerMaxBitRates
|
||||
UPNP_AddPortMapping
|
||||
UPNP_AddAnyPortMapping
|
||||
UPNP_DeletePortMapping
|
||||
UPNP_DeletePortMappingRange
|
||||
UPNP_GetPortMappingNumberOfEntries
|
||||
UPNP_GetSpecificPortMappingEntry
|
||||
UPNP_GetGenericPortMappingEntry
|
||||
UPNP_GetListOfPortMappings
|
||||
UPNP_AddPinhole
|
||||
UPNP_CheckPinholeWorking
|
||||
UPNP_UpdatePinhole
|
||||
UPNP_GetPinholePackets
|
||||
UPNP_DeletePinhole
|
||||
UPNP_GetFirewallStatus
|
||||
UPNP_GetOutboundPinholeTimeout
|
||||
; upnperrors
|
||||
strupnperror
|
||||
; portlistingparse
|
||||
ParsePortListing
|
||||
FreePortListing
|
152
deps/miniupnpc/miniupnpc.h
vendored
Normal file
152
deps/miniupnpc/miniupnpc.h
vendored
Normal file
@ -0,0 +1,152 @@
|
||||
/* $Id: miniupnpc.h,v 1.50 2016/04/19 21:06:21 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2016 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPC_H_INCLUDED
|
||||
#define MINIUPNPC_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
#include "igd_desc_parse.h"
|
||||
#include "upnpdev.h"
|
||||
|
||||
/* error codes : */
|
||||
#define UPNPDISCOVER_SUCCESS (0)
|
||||
#define UPNPDISCOVER_UNKNOWN_ERROR (-1)
|
||||
#define UPNPDISCOVER_SOCKET_ERROR (-101)
|
||||
#define UPNPDISCOVER_MEMORY_ERROR (-102)
|
||||
|
||||
/* versions : */
|
||||
#define MINIUPNPC_VERSION "2.0"
|
||||
#define MINIUPNPC_API_VERSION 16
|
||||
|
||||
/* Source port:
|
||||
Using "1" as an alias for 1900 for backwards compatability
|
||||
(presuming one would have used that for the "sameport" parameter) */
|
||||
#define UPNP_LOCAL_PORT_ANY 0
|
||||
#define UPNP_LOCAL_PORT_SAME 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Structures definitions : */
|
||||
struct UPNParg { const char * elt; const char * val; };
|
||||
|
||||
char *
|
||||
simpleUPnPcommand(int, const char *, const char *,
|
||||
const char *, struct UPNParg *,
|
||||
int *);
|
||||
|
||||
/* upnpDiscover()
|
||||
* discover UPnP devices on the network.
|
||||
* The discovered devices are returned as a chained list.
|
||||
* It is up to the caller to free the list with freeUPNPDevlist().
|
||||
* delay (in millisecond) is the maximum time for waiting any device
|
||||
* response.
|
||||
* If available, device list will be obtained from MiniSSDPd.
|
||||
* Default path for minissdpd socket will be used if minissdpdsock argument
|
||||
* is NULL.
|
||||
* If multicastif is not NULL, it will be used instead of the default
|
||||
* multicast interface for sending SSDP discover packets.
|
||||
* If localport is set to UPNP_LOCAL_PORT_SAME(1) SSDP packets will be sent
|
||||
* from the source port 1900 (same as destination port), if set to
|
||||
* UPNP_LOCAL_PORT_ANY(0) system assign a source port, any other value will
|
||||
* be attempted as the source port.
|
||||
* "searchalltypes" parameter is useful when searching several types,
|
||||
* if 0, the discovery will stop with the first type returning results.
|
||||
* TTL should default to 2. */
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscover(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverAll(int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevice(const char * device, int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error);
|
||||
|
||||
MINIUPNP_LIBSPEC struct UPNPDev *
|
||||
upnpDiscoverDevices(const char * const deviceTypes[],
|
||||
int delay, const char * multicastif,
|
||||
const char * minissdpdsock, int localport,
|
||||
int ipv6, unsigned char ttl,
|
||||
int * error,
|
||||
int searchalltypes);
|
||||
|
||||
/* parserootdesc() :
|
||||
* parse root XML description of a UPnP device and fill the IGDdatas
|
||||
* structure. */
|
||||
MINIUPNP_LIBSPEC void parserootdesc(const char *, int, struct IGDdatas *);
|
||||
|
||||
/* structure used to get fast access to urls
|
||||
* controlURL: controlURL of the WANIPConnection
|
||||
* ipcondescURL: url of the description of the WANIPConnection
|
||||
* controlURL_CIF: controlURL of the WANCommonInterfaceConfig
|
||||
* controlURL_6FC: controlURL of the WANIPv6FirewallControl
|
||||
*/
|
||||
struct UPNPUrls {
|
||||
char * controlURL;
|
||||
char * ipcondescURL;
|
||||
char * controlURL_CIF;
|
||||
char * controlURL_6FC;
|
||||
char * rootdescURL;
|
||||
};
|
||||
|
||||
/* UPNP_GetValidIGD() :
|
||||
* return values :
|
||||
* 0 = NO IGD found
|
||||
* 1 = A valid connected IGD has been found
|
||||
* 2 = A valid IGD has been found but it reported as
|
||||
* not connected
|
||||
* 3 = an UPnP device has been found but was not recognized as an IGD
|
||||
*
|
||||
* In any non zero return case, the urls and data structures
|
||||
* passed as parameters are set. Donc forget to call FreeUPNPUrls(urls) to
|
||||
* free allocated memory.
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetValidIGD(struct UPNPDev * devlist,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen);
|
||||
|
||||
/* UPNP_GetIGDFromUrl()
|
||||
* Used when skipping the discovery process.
|
||||
* When succeding, urls, data, and lanaddr arguments are set.
|
||||
* return value :
|
||||
* 0 - Not ok
|
||||
* 1 - OK */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetIGDFromUrl(const char * rootdescurl,
|
||||
struct UPNPUrls * urls,
|
||||
struct IGDdatas * data,
|
||||
char * lanaddr, int lanaddrlen);
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
GetUPNPUrls(struct UPNPUrls *, struct IGDdatas *,
|
||||
const char *, unsigned int);
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
FreeUPNPUrls(struct UPNPUrls *);
|
||||
|
||||
/* return 0 or 1 */
|
||||
MINIUPNP_LIBSPEC int UPNPIGD_IsConnected(struct UPNPUrls *, struct IGDdatas *);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
12
deps/miniupnpc/miniupnpc_declspec.h
vendored
Normal file
12
deps/miniupnpc/miniupnpc_declspec.h
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef MINIUPNPC_DECLSPEC_H_INCLUDED
|
||||
#define MINIUPNPC_DECLSPEC_H_INCLUDED
|
||||
|
||||
#if defined(__GNUC__) && __GNUC__ >= 4
|
||||
/* fix dynlib for OS X 10.9.2 and Apple LLVM version 5.0 */
|
||||
#define MINIUPNP_LIBSPEC __attribute__ ((visibility ("default")))
|
||||
#else
|
||||
#define MINIUPNP_LIBSPEC
|
||||
#endif
|
||||
|
||||
#endif /* MINIUPNPC_DECLSPEC_H_INCLUDED */
|
||||
|
23
deps/miniupnpc/miniupnpcstrings.h
vendored
Normal file
23
deps/miniupnpc/miniupnpcstrings.h
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/* $Id: miniupnpcstrings.h.in,v 1.6 2014/11/04 22:31:55 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2005-2014 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPCSTRINGS_H_INCLUDED
|
||||
#define MINIUPNPCSTRINGS_H_INCLUDED
|
||||
|
||||
#define OS_STRING "OS/version"
|
||||
#define MINIUPNPC_VERSION_STRING "version"
|
||||
|
||||
#if 0
|
||||
/* according to "UPnP Device Architecture 1.0" */
|
||||
#define UPNP_VERSION_STRING "UPnP/1.0"
|
||||
#else
|
||||
/* according to "UPnP Device Architecture 1.1" */
|
||||
#define UPNP_VERSION_STRING "UPnP/1.1"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
19
deps/miniupnpc/miniupnpctypes.h
vendored
Normal file
19
deps/miniupnpc/miniupnpctypes.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/* $Id: miniupnpctypes.h,v 1.1 2011/02/15 11:10:40 nanard Exp $ */
|
||||
/* Miniupnp project : http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2011 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided within this distribution */
|
||||
#ifndef MINIUPNPCTYPES_H_INCLUDED
|
||||
#define MINIUPNPCTYPES_H_INCLUDED
|
||||
|
||||
#if (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)
|
||||
#define UNSIGNED_INTEGER unsigned long long
|
||||
#define STRTOUI strtoull
|
||||
#else
|
||||
#define UNSIGNED_INTEGER unsigned int
|
||||
#define STRTOUI strtoul
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
661
deps/miniupnpc/miniwget.c
vendored
Normal file
661
deps/miniupnpc/miniwget.c
vendored
Normal file
@ -0,0 +1,661 @@
|
||||
/* $Id: miniwget.c,v 1.75 2016/01/24 17:24:36 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Website : http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2016 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <io.h>
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#define snprintf _snprintf
|
||||
#define socklen_t int
|
||||
#ifndef strncasecmp
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1400)
|
||||
#define strncasecmp _memicmp
|
||||
#else /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#define strncasecmp memicmp
|
||||
#endif /* defined(_MSC_VER) && (_MSC_VER >= 1400) */
|
||||
#endif /* #ifndef strncasecmp */
|
||||
#else /* #ifdef _WIN32 */
|
||||
#include <unistd.h>
|
||||
#include <sys/param.h>
|
||||
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||
#define socklen_t int
|
||||
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#include <sys/select.h>
|
||||
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <net/if.h>
|
||||
#include <netdb.h>
|
||||
#define closesocket close
|
||||
#include <strings.h>
|
||||
#endif /* #else _WIN32 */
|
||||
#ifdef __GNU__
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif /* __GNU__ */
|
||||
|
||||
#ifndef MIN
|
||||
#define MIN(x,y) (((x)<(y))?(x):(y))
|
||||
#endif /* MIN */
|
||||
|
||||
|
||||
#include "miniupnpcstrings.h"
|
||||
#include "miniwget.h"
|
||||
#include "connecthostport.h"
|
||||
#include "receivedata.h"
|
||||
|
||||
#ifndef MAXHOSTNAMELEN
|
||||
#define MAXHOSTNAMELEN 64
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Read a HTTP response from a socket.
|
||||
* Process Content-Length and Transfer-encoding headers.
|
||||
* return a pointer to the content buffer, which length is saved
|
||||
* to the length parameter.
|
||||
*/
|
||||
void *
|
||||
getHTTPResponse(int s, int * size, int * status_code)
|
||||
{
|
||||
char buf[2048];
|
||||
int n;
|
||||
int endofheaders = 0;
|
||||
int chunked = 0;
|
||||
int content_length = -1;
|
||||
unsigned int chunksize = 0;
|
||||
unsigned int bytestocopy = 0;
|
||||
/* buffers : */
|
||||
char * header_buf;
|
||||
unsigned int header_buf_len = 2048;
|
||||
unsigned int header_buf_used = 0;
|
||||
char * content_buf;
|
||||
unsigned int content_buf_len = 2048;
|
||||
unsigned int content_buf_used = 0;
|
||||
char chunksize_buf[32];
|
||||
unsigned int chunksize_buf_index;
|
||||
#ifdef DEBUG
|
||||
char * reason_phrase = NULL;
|
||||
int reason_phrase_len = 0;
|
||||
#endif
|
||||
|
||||
if(status_code) *status_code = -1;
|
||||
header_buf = malloc(header_buf_len);
|
||||
if(header_buf == NULL)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
|
||||
#endif /* DEBUG */
|
||||
*size = -1;
|
||||
return NULL;
|
||||
}
|
||||
content_buf = malloc(content_buf_len);
|
||||
if(content_buf == NULL)
|
||||
{
|
||||
free(header_buf);
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: Memory allocation error\n", "getHTTPResponse");
|
||||
#endif /* DEBUG */
|
||||
*size = -1;
|
||||
return NULL;
|
||||
}
|
||||
chunksize_buf[0] = '\0';
|
||||
chunksize_buf_index = 0;
|
||||
|
||||
while((n = receivedata(s, buf, 2048, 5000, NULL)) > 0)
|
||||
{
|
||||
if(endofheaders == 0)
|
||||
{
|
||||
int i;
|
||||
int linestart=0;
|
||||
int colon=0;
|
||||
int valuestart=0;
|
||||
if(header_buf_used + n > header_buf_len) {
|
||||
char * tmp = realloc(header_buf, header_buf_used + n);
|
||||
if(tmp == NULL) {
|
||||
/* memory allocation error */
|
||||
free(header_buf);
|
||||
free(content_buf);
|
||||
*size = -1;
|
||||
return NULL;
|
||||
}
|
||||
header_buf = tmp;
|
||||
header_buf_len = header_buf_used + n;
|
||||
}
|
||||
memcpy(header_buf + header_buf_used, buf, n);
|
||||
header_buf_used += n;
|
||||
/* search for CR LF CR LF (end of headers)
|
||||
* recognize also LF LF */
|
||||
i = 0;
|
||||
while(i < ((int)header_buf_used-1) && (endofheaders == 0)) {
|
||||
if(header_buf[i] == '\r') {
|
||||
i++;
|
||||
if(header_buf[i] == '\n') {
|
||||
i++;
|
||||
if(i < (int)header_buf_used && header_buf[i] == '\r') {
|
||||
i++;
|
||||
if(i < (int)header_buf_used && header_buf[i] == '\n') {
|
||||
endofheaders = i+1;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(header_buf[i] == '\n') {
|
||||
i++;
|
||||
if(header_buf[i] == '\n') {
|
||||
endofheaders = i+1;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if(endofheaders == 0)
|
||||
continue;
|
||||
/* parse header lines */
|
||||
for(i = 0; i < endofheaders - 1; i++) {
|
||||
if(linestart > 0 && colon <= linestart && header_buf[i]==':')
|
||||
{
|
||||
colon = i;
|
||||
while(i < (endofheaders-1)
|
||||
&& (header_buf[i+1] == ' ' || header_buf[i+1] == '\t'))
|
||||
i++;
|
||||
valuestart = i + 1;
|
||||
}
|
||||
/* detecting end of line */
|
||||
else if(header_buf[i]=='\r' || header_buf[i]=='\n')
|
||||
{
|
||||
if(linestart == 0 && status_code)
|
||||
{
|
||||
/* Status line
|
||||
* HTTP-Version SP Status-Code SP Reason-Phrase CRLF */
|
||||
int sp;
|
||||
for(sp = 0; sp < i; sp++)
|
||||
if(header_buf[sp] == ' ')
|
||||
{
|
||||
if(*status_code < 0)
|
||||
*status_code = atoi(header_buf + sp + 1);
|
||||
else
|
||||
{
|
||||
#ifdef DEBUG
|
||||
reason_phrase = header_buf + sp + 1;
|
||||
reason_phrase_len = i - sp - 1;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("HTTP status code = %d, Reason phrase = %.*s\n",
|
||||
*status_code, reason_phrase_len, reason_phrase);
|
||||
#endif
|
||||
}
|
||||
else if(colon > linestart && valuestart > colon)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("header='%.*s', value='%.*s'\n",
|
||||
colon-linestart, header_buf+linestart,
|
||||
i-valuestart, header_buf+valuestart);
|
||||
#endif
|
||||
if(0==strncasecmp(header_buf+linestart, "content-length", colon-linestart))
|
||||
{
|
||||
content_length = atoi(header_buf+valuestart);
|
||||
#ifdef DEBUG
|
||||
printf("Content-Length: %d\n", content_length);
|
||||
#endif
|
||||
}
|
||||
else if(0==strncasecmp(header_buf+linestart, "transfer-encoding", colon-linestart)
|
||||
&& 0==strncasecmp(header_buf+valuestart, "chunked", 7))
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("chunked transfer-encoding!\n");
|
||||
#endif
|
||||
chunked = 1;
|
||||
}
|
||||
}
|
||||
while((i < (int)header_buf_used) && (header_buf[i]=='\r' || header_buf[i] == '\n'))
|
||||
i++;
|
||||
linestart = i;
|
||||
colon = linestart;
|
||||
valuestart = 0;
|
||||
}
|
||||
}
|
||||
/* copy the remaining of the received data back to buf */
|
||||
n = header_buf_used - endofheaders;
|
||||
memcpy(buf, header_buf + endofheaders, n);
|
||||
/* if(headers) */
|
||||
}
|
||||
if(endofheaders)
|
||||
{
|
||||
/* content */
|
||||
if(chunked)
|
||||
{
|
||||
int i = 0;
|
||||
while(i < n)
|
||||
{
|
||||
if(chunksize == 0)
|
||||
{
|
||||
/* reading chunk size */
|
||||
if(chunksize_buf_index == 0) {
|
||||
/* skipping any leading CR LF */
|
||||
if(i<n && buf[i] == '\r') i++;
|
||||
if(i<n && buf[i] == '\n') i++;
|
||||
}
|
||||
while(i<n && isxdigit(buf[i])
|
||||
&& chunksize_buf_index < (sizeof(chunksize_buf)-1))
|
||||
{
|
||||
chunksize_buf[chunksize_buf_index++] = buf[i];
|
||||
chunksize_buf[chunksize_buf_index] = '\0';
|
||||
i++;
|
||||
}
|
||||
while(i<n && buf[i] != '\r' && buf[i] != '\n')
|
||||
i++; /* discarding chunk-extension */
|
||||
if(i<n && buf[i] == '\r') i++;
|
||||
if(i<n && buf[i] == '\n') {
|
||||
unsigned int j;
|
||||
for(j = 0; j < chunksize_buf_index; j++) {
|
||||
if(chunksize_buf[j] >= '0'
|
||||
&& chunksize_buf[j] <= '9')
|
||||
chunksize = (chunksize << 4) + (chunksize_buf[j] - '0');
|
||||
else
|
||||
chunksize = (chunksize << 4) + ((chunksize_buf[j] | 32) - 'a' + 10);
|
||||
}
|
||||
chunksize_buf[0] = '\0';
|
||||
chunksize_buf_index = 0;
|
||||
i++;
|
||||
} else {
|
||||
/* not finished to get chunksize */
|
||||
continue;
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("chunksize = %u (%x)\n", chunksize, chunksize);
|
||||
#endif
|
||||
if(chunksize == 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("end of HTTP content - %d %d\n", i, n);
|
||||
/*printf("'%.*s'\n", n-i, buf+i);*/
|
||||
#endif
|
||||
goto end_of_stream;
|
||||
}
|
||||
}
|
||||
bytestocopy = ((int)chunksize < (n - i))?chunksize:(unsigned int)(n - i);
|
||||
if((content_buf_used + bytestocopy) > content_buf_len)
|
||||
{
|
||||
char * tmp;
|
||||
if(content_length >= (int)(content_buf_used + bytestocopy)) {
|
||||
content_buf_len = content_length;
|
||||
} else {
|
||||
content_buf_len = content_buf_used + bytestocopy;
|
||||
}
|
||||
tmp = realloc(content_buf, content_buf_len);
|
||||
if(tmp == NULL) {
|
||||
/* memory allocation error */
|
||||
free(content_buf);
|
||||
free(header_buf);
|
||||
*size = -1;
|
||||
return NULL;
|
||||
}
|
||||
content_buf = tmp;
|
||||
}
|
||||
memcpy(content_buf + content_buf_used, buf + i, bytestocopy);
|
||||
content_buf_used += bytestocopy;
|
||||
i += bytestocopy;
|
||||
chunksize -= bytestocopy;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* not chunked */
|
||||
if(content_length > 0
|
||||
&& (int)(content_buf_used + n) > content_length) {
|
||||
/* skipping additional bytes */
|
||||
n = content_length - content_buf_used;
|
||||
}
|
||||
if(content_buf_used + n > content_buf_len)
|
||||
{
|
||||
char * tmp;
|
||||
if(content_length >= (int)(content_buf_used + n)) {
|
||||
content_buf_len = content_length;
|
||||
} else {
|
||||
content_buf_len = content_buf_used + n;
|
||||
}
|
||||
tmp = realloc(content_buf, content_buf_len);
|
||||
if(tmp == NULL) {
|
||||
/* memory allocation error */
|
||||
free(content_buf);
|
||||
free(header_buf);
|
||||
*size = -1;
|
||||
return NULL;
|
||||
}
|
||||
content_buf = tmp;
|
||||
}
|
||||
memcpy(content_buf + content_buf_used, buf, n);
|
||||
content_buf_used += n;
|
||||
}
|
||||
}
|
||||
/* use the Content-Length header value if available */
|
||||
if(content_length > 0 && (int)content_buf_used >= content_length)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("End of HTTP content\n");
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
end_of_stream:
|
||||
free(header_buf); header_buf = NULL;
|
||||
*size = content_buf_used;
|
||||
if(content_buf_used == 0)
|
||||
{
|
||||
free(content_buf);
|
||||
content_buf = NULL;
|
||||
}
|
||||
return content_buf;
|
||||
}
|
||||
|
||||
/* miniwget3() :
|
||||
* do all the work.
|
||||
* Return NULL if something failed. */
|
||||
static void *
|
||||
miniwget3(const char * host,
|
||||
unsigned short port, const char * path,
|
||||
int * size, char * addr_str, int addr_str_len,
|
||||
const char * httpversion, unsigned int scope_id,
|
||||
int * status_code)
|
||||
{
|
||||
char buf[2048];
|
||||
int s;
|
||||
int n;
|
||||
int len;
|
||||
int sent;
|
||||
void * content;
|
||||
|
||||
*size = 0;
|
||||
s = connecthostport(host, port, scope_id);
|
||||
if(s < 0)
|
||||
return NULL;
|
||||
|
||||
/* get address for caller ! */
|
||||
if(addr_str)
|
||||
{
|
||||
struct sockaddr_storage saddr;
|
||||
socklen_t saddrlen;
|
||||
|
||||
saddrlen = sizeof(saddr);
|
||||
if(getsockname(s, (struct sockaddr *)&saddr, &saddrlen) < 0)
|
||||
{
|
||||
perror("getsockname");
|
||||
}
|
||||
else
|
||||
{
|
||||
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* using INT WINAPI WSAAddressToStringA(LPSOCKADDR, DWORD, LPWSAPROTOCOL_INFOA, LPSTR, LPDWORD);
|
||||
* But his function make a string with the port : nn.nn.nn.nn:port */
|
||||
/* if(WSAAddressToStringA((SOCKADDR *)&saddr, sizeof(saddr),
|
||||
NULL, addr_str, (DWORD *)&addr_str_len))
|
||||
{
|
||||
printf("WSAAddressToStringA() failed : %d\n", WSAGetLastError());
|
||||
}*/
|
||||
/* the following code is only compatible with ip v4 addresses */
|
||||
strncpy(addr_str, inet_ntoa(((struct sockaddr_in *)&saddr)->sin_addr), addr_str_len);
|
||||
#else
|
||||
#if 0
|
||||
if(saddr.sa_family == AF_INET6) {
|
||||
inet_ntop(AF_INET6,
|
||||
&(((struct sockaddr_in6 *)&saddr)->sin6_addr),
|
||||
addr_str, addr_str_len);
|
||||
} else {
|
||||
inet_ntop(AF_INET,
|
||||
&(((struct sockaddr_in *)&saddr)->sin_addr),
|
||||
addr_str, addr_str_len);
|
||||
}
|
||||
#endif
|
||||
/* getnameinfo return ip v6 address with the scope identifier
|
||||
* such as : 2a01:e35:8b2b:7330::%4281128194 */
|
||||
n = getnameinfo((const struct sockaddr *)&saddr, saddrlen,
|
||||
addr_str, addr_str_len,
|
||||
NULL, 0,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if(n != 0) {
|
||||
#ifdef _WIN32
|
||||
fprintf(stderr, "getnameinfo() failed : %d\n", n);
|
||||
#else
|
||||
fprintf(stderr, "getnameinfo() failed : %s\n", gai_strerror(n));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printf("address miniwget : %s\n", addr_str);
|
||||
#endif
|
||||
}
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"GET %s HTTP/%s\r\n"
|
||||
"Host: %s:%d\r\n"
|
||||
"Connection: Close\r\n"
|
||||
"User-Agent: " OS_STRING ", " UPNP_VERSION_STRING ", MiniUPnPc/" MINIUPNPC_VERSION_STRING "\r\n"
|
||||
|
||||
"\r\n",
|
||||
path, httpversion, host, port);
|
||||
if ((unsigned int)len >= sizeof(buf))
|
||||
{
|
||||
closesocket(s);
|
||||
return NULL;
|
||||
}
|
||||
sent = 0;
|
||||
/* sending the HTTP request */
|
||||
while(sent < len)
|
||||
{
|
||||
n = send(s, buf+sent, len-sent, 0);
|
||||
if(n < 0)
|
||||
{
|
||||
perror("send");
|
||||
closesocket(s);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
sent += n;
|
||||
}
|
||||
}
|
||||
content = getHTTPResponse(s, size, status_code);
|
||||
closesocket(s);
|
||||
return content;
|
||||
}
|
||||
|
||||
/* miniwget2() :
|
||||
* Call miniwget3(); retry with HTTP/1.1 if 1.0 fails. */
|
||||
static void *
|
||||
miniwget2(const char * host,
|
||||
unsigned short port, const char * path,
|
||||
int * size, char * addr_str, int addr_str_len,
|
||||
unsigned int scope_id, int * status_code)
|
||||
{
|
||||
char * respbuffer;
|
||||
|
||||
#if 1
|
||||
respbuffer = miniwget3(host, port, path, size,
|
||||
addr_str, addr_str_len, "1.1",
|
||||
scope_id, status_code);
|
||||
#else
|
||||
respbuffer = miniwget3(host, port, path, size,
|
||||
addr_str, addr_str_len, "1.0",
|
||||
scope_id, status_code);
|
||||
if (*size == 0)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("Retrying with HTTP/1.1\n");
|
||||
#endif
|
||||
free(respbuffer);
|
||||
respbuffer = miniwget3(host, port, path, size,
|
||||
addr_str, addr_str_len, "1.1",
|
||||
scope_id, status_code);
|
||||
}
|
||||
#endif
|
||||
return respbuffer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* parseURL()
|
||||
* arguments :
|
||||
* url : source string not modified
|
||||
* hostname : hostname destination string (size of MAXHOSTNAMELEN+1)
|
||||
* port : port (destination)
|
||||
* path : pointer to the path part of the URL
|
||||
*
|
||||
* Return values :
|
||||
* 0 - Failure
|
||||
* 1 - Success */
|
||||
int
|
||||
parseURL(const char * url,
|
||||
char * hostname, unsigned short * port,
|
||||
char * * path, unsigned int * scope_id)
|
||||
{
|
||||
char * p1, *p2, *p3;
|
||||
if(!url)
|
||||
return 0;
|
||||
p1 = strstr(url, "://");
|
||||
if(!p1)
|
||||
return 0;
|
||||
p1 += 3;
|
||||
if( (url[0]!='h') || (url[1]!='t')
|
||||
||(url[2]!='t') || (url[3]!='p'))
|
||||
return 0;
|
||||
memset(hostname, 0, MAXHOSTNAMELEN + 1);
|
||||
if(*p1 == '[')
|
||||
{
|
||||
/* IP v6 : http://[2a00:1450:8002::6a]/path/abc */
|
||||
char * scope;
|
||||
scope = strchr(p1, '%');
|
||||
p2 = strchr(p1, ']');
|
||||
if(p2 && scope && scope < p2 && scope_id) {
|
||||
/* parse scope */
|
||||
#ifdef IF_NAMESIZE
|
||||
char tmp[IF_NAMESIZE];
|
||||
int l;
|
||||
scope++;
|
||||
/* "%25" is just '%' in URL encoding */
|
||||
if(scope[0] == '2' && scope[1] == '5')
|
||||
scope += 2; /* skip "25" */
|
||||
l = p2 - scope;
|
||||
if(l >= IF_NAMESIZE)
|
||||
l = IF_NAMESIZE - 1;
|
||||
memcpy(tmp, scope, l);
|
||||
tmp[l] = '\0';
|
||||
*scope_id = if_nametoindex(tmp);
|
||||
if(*scope_id == 0) {
|
||||
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
|
||||
}
|
||||
#else
|
||||
/* under windows, scope is numerical */
|
||||
char tmp[8];
|
||||
int l;
|
||||
scope++;
|
||||
/* "%25" is just '%' in URL encoding */
|
||||
if(scope[0] == '2' && scope[1] == '5')
|
||||
scope += 2; /* skip "25" */
|
||||
l = p2 - scope;
|
||||
if(l >= sizeof(tmp))
|
||||
l = sizeof(tmp) - 1;
|
||||
memcpy(tmp, scope, l);
|
||||
tmp[l] = '\0';
|
||||
*scope_id = (unsigned int)strtoul(tmp, NULL, 10);
|
||||
#endif
|
||||
}
|
||||
p3 = strchr(p1, '/');
|
||||
if(p2 && p3)
|
||||
{
|
||||
p2++;
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
|
||||
if(*p2 == ':')
|
||||
{
|
||||
*port = 0;
|
||||
p2++;
|
||||
while( (*p2 >= '0') && (*p2 <= '9'))
|
||||
{
|
||||
*port *= 10;
|
||||
*port += (unsigned short)(*p2 - '0');
|
||||
p2++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*port = 80;
|
||||
}
|
||||
*path = p3;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
p2 = strchr(p1, ':');
|
||||
p3 = strchr(p1, '/');
|
||||
if(!p3)
|
||||
return 0;
|
||||
if(!p2 || (p2>p3))
|
||||
{
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p3-p1)));
|
||||
*port = 80;
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(hostname, p1, MIN(MAXHOSTNAMELEN, (int)(p2-p1)));
|
||||
*port = 0;
|
||||
p2++;
|
||||
while( (*p2 >= '0') && (*p2 <= '9'))
|
||||
{
|
||||
*port *= 10;
|
||||
*port += (unsigned short)(*p2 - '0');
|
||||
p2++;
|
||||
}
|
||||
}
|
||||
*path = p3;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void *
|
||||
miniwget(const char * url, int * size,
|
||||
unsigned int scope_id, int * status_code)
|
||||
{
|
||||
unsigned short port;
|
||||
char * path;
|
||||
/* protocol://host:port/chemin */
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
*size = 0;
|
||||
if(!parseURL(url, hostname, &port, &path, &scope_id))
|
||||
return NULL;
|
||||
#ifdef DEBUG
|
||||
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
|
||||
hostname, port, path, scope_id);
|
||||
#endif
|
||||
return miniwget2(hostname, port, path, size, 0, 0, scope_id, status_code);
|
||||
}
|
||||
|
||||
void *
|
||||
miniwget_getaddr(const char * url, int * size,
|
||||
char * addr, int addrlen, unsigned int scope_id,
|
||||
int * status_code)
|
||||
{
|
||||
unsigned short port;
|
||||
char * path;
|
||||
/* protocol://host:port/path */
|
||||
char hostname[MAXHOSTNAMELEN+1];
|
||||
*size = 0;
|
||||
if(addr)
|
||||
addr[0] = '\0';
|
||||
if(!parseURL(url, hostname, &port, &path, &scope_id))
|
||||
return NULL;
|
||||
#ifdef DEBUG
|
||||
printf("parsed url : hostname='%s' port=%hu path='%s' scope_id=%u\n",
|
||||
hostname, port, path, scope_id);
|
||||
#endif
|
||||
return miniwget2(hostname, port, path, size, addr, addrlen, scope_id, status_code);
|
||||
}
|
||||
|
30
deps/miniupnpc/miniwget.h
vendored
Normal file
30
deps/miniupnpc/miniwget.h
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
/* $Id: miniwget.h,v 1.12 2016/01/24 17:24:36 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2016 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef MINIWGET_H_INCLUDED
|
||||
#define MINIWGET_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
MINIUPNP_LIBSPEC void * getHTTPResponse(int s, int * size, int * status_code);
|
||||
|
||||
MINIUPNP_LIBSPEC void * miniwget(const char *, int *, unsigned int, int *);
|
||||
|
||||
MINIUPNP_LIBSPEC void * miniwget_getaddr(const char *, int *, char *, int, unsigned int, int *);
|
||||
|
||||
int parseURL(const char *, char *, unsigned short *, char * *, unsigned int *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
229
deps/miniupnpc/minixml.c
vendored
Normal file
229
deps/miniupnpc/minixml.c
vendored
Normal file
@ -0,0 +1,229 @@
|
||||
/* $Id: minixml.c,v 1.10 2012/03/05 19:42:47 nanard Exp $ */
|
||||
/* minixml.c : the minimum size a xml parser can be ! */
|
||||
/* Project : miniupnp
|
||||
* webpage: http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author : Thomas Bernard
|
||||
|
||||
Copyright (c) 2005-2014, Thomas BERNARD
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "minixml.h"
|
||||
|
||||
/* parseatt : used to parse the argument list
|
||||
* return 0 (false) in case of success and -1 (true) if the end
|
||||
* of the xmlbuffer is reached. */
|
||||
static int parseatt(struct xmlparser * p)
|
||||
{
|
||||
const char * attname;
|
||||
int attnamelen;
|
||||
const char * attvalue;
|
||||
int attvaluelen;
|
||||
while(p->xml < p->xmlend)
|
||||
{
|
||||
if(*p->xml=='/' || *p->xml=='>')
|
||||
return 0;
|
||||
if( !IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
char sep;
|
||||
attname = p->xml;
|
||||
attnamelen = 0;
|
||||
while(*p->xml!='=' && !IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
attnamelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
while(*(p->xml++) != '=')
|
||||
{
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
while(IS_WHITE_SPACE(*p->xml))
|
||||
{
|
||||
p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
sep = *p->xml;
|
||||
if(sep=='\'' || sep=='\"')
|
||||
{
|
||||
p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
attvalue = p->xml;
|
||||
attvaluelen = 0;
|
||||
while(*p->xml != sep)
|
||||
{
|
||||
attvaluelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attvalue = p->xml;
|
||||
attvaluelen = 0;
|
||||
while( !IS_WHITE_SPACE(*p->xml)
|
||||
&& *p->xml != '>' && *p->xml != '/')
|
||||
{
|
||||
attvaluelen++; p->xml++;
|
||||
if(p->xml >= p->xmlend)
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
/*printf("%.*s='%.*s'\n",
|
||||
attnamelen, attname, attvaluelen, attvalue);*/
|
||||
if(p->attfunc)
|
||||
p->attfunc(p->data, attname, attnamelen, attvalue, attvaluelen);
|
||||
}
|
||||
p->xml++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* parseelt parse the xml stream and
|
||||
* call the callback functions when needed... */
|
||||
static void parseelt(struct xmlparser * p)
|
||||
{
|
||||
int i;
|
||||
const char * elementname;
|
||||
while(p->xml < (p->xmlend - 1))
|
||||
{
|
||||
if((p->xml + 4) <= p->xmlend && (0 == memcmp(p->xml, "<!--", 4)))
|
||||
{
|
||||
p->xml += 3;
|
||||
/* ignore comments */
|
||||
do
|
||||
{
|
||||
p->xml++;
|
||||
if ((p->xml + 3) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
while(memcmp(p->xml, "-->", 3) != 0);
|
||||
p->xml += 3;
|
||||
}
|
||||
else if((p->xml)[0]=='<' && (p->xml)[1]!='?')
|
||||
{
|
||||
i = 0; elementname = ++p->xml;
|
||||
while( !IS_WHITE_SPACE(*p->xml)
|
||||
&& (*p->xml!='>') && (*p->xml!='/')
|
||||
)
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
/* to ignore namespace : */
|
||||
if(*p->xml==':')
|
||||
{
|
||||
i = 0;
|
||||
elementname = ++p->xml;
|
||||
}
|
||||
}
|
||||
if(i>0)
|
||||
{
|
||||
if(p->starteltfunc)
|
||||
p->starteltfunc(p->data, elementname, i);
|
||||
if(parseatt(p))
|
||||
return;
|
||||
if(*p->xml!='/')
|
||||
{
|
||||
const char * data;
|
||||
i = 0; data = ++p->xml;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
while( IS_WHITE_SPACE(*p->xml) )
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(memcmp(p->xml, "<![CDATA[", 9) == 0)
|
||||
{
|
||||
/* CDATA handling */
|
||||
p->xml += 9;
|
||||
data = p->xml;
|
||||
i = 0;
|
||||
while(memcmp(p->xml, "]]>", 3) != 0)
|
||||
{
|
||||
i++; p->xml++;
|
||||
if ((p->xml + 3) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(i>0 && p->datafunc)
|
||||
p->datafunc(p->data, data, i);
|
||||
while(*p->xml!='<')
|
||||
{
|
||||
p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while(*p->xml!='<')
|
||||
{
|
||||
i++; p->xml++;
|
||||
if ((p->xml + 1) >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(i>0 && p->datafunc && *(p->xml + 1) == '/')
|
||||
p->datafunc(p->data, data, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(*p->xml == '/')
|
||||
{
|
||||
i = 0; elementname = ++p->xml;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
while((*p->xml != '>'))
|
||||
{
|
||||
i++; p->xml++;
|
||||
if (p->xml >= p->xmlend)
|
||||
return;
|
||||
}
|
||||
if(p->endeltfunc)
|
||||
p->endeltfunc(p->data, elementname, i);
|
||||
p->xml++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
p->xml++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* the parser must be initialized before calling this function */
|
||||
void parsexml(struct xmlparser * parser)
|
||||
{
|
||||
parser->xml = parser->xmlstart;
|
||||
parser->xmlend = parser->xmlstart + parser->xmlsize;
|
||||
parseelt(parser);
|
||||
}
|
||||
|
||||
|
37
deps/miniupnpc/minixml.h
vendored
Normal file
37
deps/miniupnpc/minixml.h
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
/* $Id: minixml.h,v 1.6 2006/11/30 11:47:21 nanard Exp $ */
|
||||
/* minimal xml parser
|
||||
*
|
||||
* Project : miniupnp
|
||||
* Website : http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution.
|
||||
* */
|
||||
#ifndef MINIXML_H_INCLUDED
|
||||
#define MINIXML_H_INCLUDED
|
||||
#define IS_WHITE_SPACE(c) ((c==' ') || (c=='\t') || (c=='\r') || (c=='\n'))
|
||||
|
||||
/* if a callback function pointer is set to NULL,
|
||||
* the function is not called */
|
||||
struct xmlparser {
|
||||
const char *xmlstart;
|
||||
const char *xmlend;
|
||||
const char *xml; /* pointer to current character */
|
||||
int xmlsize;
|
||||
void * data;
|
||||
void (*starteltfunc) (void *, const char *, int);
|
||||
void (*endeltfunc) (void *, const char *, int);
|
||||
void (*datafunc) (void *, const char *, int);
|
||||
void (*attfunc) (void *, const char *, int, const char *, int);
|
||||
};
|
||||
|
||||
/* parsexml()
|
||||
* the xmlparser structure must be initialized before the call
|
||||
* the following structure members have to be initialized :
|
||||
* xmlstart, xmlsize, data, *func
|
||||
* xml is for internal usage, xmlend is computed automatically */
|
||||
void parsexml(struct xmlparser *);
|
||||
|
||||
#endif
|
||||
|
172
deps/miniupnpc/portlistingparse.c
vendored
Normal file
172
deps/miniupnpc/portlistingparse.c
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
/* $Id: portlistingparse.c,v 1.9 2015/07/15 12:41:13 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2011-2016 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#ifdef DEBUG
|
||||
#include <stdio.h>
|
||||
#endif /* DEBUG */
|
||||
#include "portlistingparse.h"
|
||||
#include "minixml.h"
|
||||
|
||||
/* list of the elements */
|
||||
static const struct {
|
||||
const portMappingElt code;
|
||||
const char * const str;
|
||||
} elements[] = {
|
||||
{ PortMappingEntry, "PortMappingEntry"},
|
||||
{ NewRemoteHost, "NewRemoteHost"},
|
||||
{ NewExternalPort, "NewExternalPort"},
|
||||
{ NewProtocol, "NewProtocol"},
|
||||
{ NewInternalPort, "NewInternalPort"},
|
||||
{ NewInternalClient, "NewInternalClient"},
|
||||
{ NewEnabled, "NewEnabled"},
|
||||
{ NewDescription, "NewDescription"},
|
||||
{ NewLeaseTime, "NewLeaseTime"},
|
||||
{ PortMappingEltNone, NULL}
|
||||
};
|
||||
|
||||
/* Helper function */
|
||||
static UNSIGNED_INTEGER
|
||||
atoui(const char * p, int l)
|
||||
{
|
||||
UNSIGNED_INTEGER r = 0;
|
||||
while(l > 0 && *p)
|
||||
{
|
||||
if(*p >= '0' && *p <= '9')
|
||||
r = r*10 + (*p - '0');
|
||||
else
|
||||
break;
|
||||
p++;
|
||||
l--;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Start element handler */
|
||||
static void
|
||||
startelt(void * d, const char * name, int l)
|
||||
{
|
||||
int i;
|
||||
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
|
||||
pdata->curelt = PortMappingEltNone;
|
||||
for(i = 0; elements[i].str; i++)
|
||||
{
|
||||
if(strlen(elements[i].str) == (size_t)l && memcmp(name, elements[i].str, l) == 0)
|
||||
{
|
||||
pdata->curelt = elements[i].code;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(pdata->curelt == PortMappingEntry)
|
||||
{
|
||||
struct PortMapping * pm;
|
||||
pm = calloc(1, sizeof(struct PortMapping));
|
||||
if(pm == NULL)
|
||||
{
|
||||
/* malloc error */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: error allocating memory",
|
||||
"startelt");
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
pm->l_next = pdata->l_head; /* insert in list */
|
||||
pdata->l_head = pm;
|
||||
}
|
||||
}
|
||||
|
||||
/* End element handler */
|
||||
static void
|
||||
endelt(void * d, const char * name, int l)
|
||||
{
|
||||
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
|
||||
(void)name;
|
||||
(void)l;
|
||||
pdata->curelt = PortMappingEltNone;
|
||||
}
|
||||
|
||||
/* Data handler */
|
||||
static void
|
||||
data(void * d, const char * data, int l)
|
||||
{
|
||||
struct PortMapping * pm;
|
||||
struct PortMappingParserData * pdata = (struct PortMappingParserData *)d;
|
||||
pm = pdata->l_head;
|
||||
if(!pm)
|
||||
return;
|
||||
if(l > 63)
|
||||
l = 63;
|
||||
switch(pdata->curelt)
|
||||
{
|
||||
case NewRemoteHost:
|
||||
memcpy(pm->remoteHost, data, l);
|
||||
pm->remoteHost[l] = '\0';
|
||||
break;
|
||||
case NewExternalPort:
|
||||
pm->externalPort = (unsigned short)atoui(data, l);
|
||||
break;
|
||||
case NewProtocol:
|
||||
if(l > 3)
|
||||
l = 3;
|
||||
memcpy(pm->protocol, data, l);
|
||||
pm->protocol[l] = '\0';
|
||||
break;
|
||||
case NewInternalPort:
|
||||
pm->internalPort = (unsigned short)atoui(data, l);
|
||||
break;
|
||||
case NewInternalClient:
|
||||
memcpy(pm->internalClient, data, l);
|
||||
pm->internalClient[l] = '\0';
|
||||
break;
|
||||
case NewEnabled:
|
||||
pm->enabled = (unsigned char)atoui(data, l);
|
||||
break;
|
||||
case NewDescription:
|
||||
memcpy(pm->description, data, l);
|
||||
pm->description[l] = '\0';
|
||||
break;
|
||||
case NewLeaseTime:
|
||||
pm->leaseTime = atoui(data, l);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Parse the PortMappingList XML document for IGD version 2
|
||||
*/
|
||||
void
|
||||
ParsePortListing(const char * buffer, int bufsize,
|
||||
struct PortMappingParserData * pdata)
|
||||
{
|
||||
struct xmlparser parser;
|
||||
|
||||
memset(pdata, 0, sizeof(struct PortMappingParserData));
|
||||
/* init xmlparser */
|
||||
parser.xmlstart = buffer;
|
||||
parser.xmlsize = bufsize;
|
||||
parser.data = pdata;
|
||||
parser.starteltfunc = startelt;
|
||||
parser.endeltfunc = endelt;
|
||||
parser.datafunc = data;
|
||||
parser.attfunc = 0;
|
||||
parsexml(&parser);
|
||||
}
|
||||
|
||||
void
|
||||
FreePortListing(struct PortMappingParserData * pdata)
|
||||
{
|
||||
struct PortMapping * pm;
|
||||
while((pm = pdata->l_head) != NULL)
|
||||
{
|
||||
/* remove from list */
|
||||
pdata->l_head = pm->l_next;
|
||||
free(pm);
|
||||
}
|
||||
}
|
||||
|
65
deps/miniupnpc/portlistingparse.h
vendored
Normal file
65
deps/miniupnpc/portlistingparse.h
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/* $Id: portlistingparse.h,v 1.10 2014/11/01 10:37:32 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2011-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
#ifndef PORTLISTINGPARSE_H_INCLUDED
|
||||
#define PORTLISTINGPARSE_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
/* for the definition of UNSIGNED_INTEGER */
|
||||
#include "miniupnpctypes.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* sample of PortMappingEntry :
|
||||
<p:PortMappingEntry>
|
||||
<p:NewRemoteHost>202.233.2.1</p:NewRemoteHost>
|
||||
<p:NewExternalPort>2345</p:NewExternalPort>
|
||||
<p:NewProtocol>TCP</p:NewProtocol>
|
||||
<p:NewInternalPort>2345</p:NewInternalPort>
|
||||
<p:NewInternalClient>192.168.1.137</p:NewInternalClient>
|
||||
<p:NewEnabled>1</p:NewEnabled>
|
||||
<p:NewDescription>dooom</p:NewDescription>
|
||||
<p:NewLeaseTime>345</p:NewLeaseTime>
|
||||
</p:PortMappingEntry>
|
||||
*/
|
||||
typedef enum { PortMappingEltNone,
|
||||
PortMappingEntry, NewRemoteHost,
|
||||
NewExternalPort, NewProtocol,
|
||||
NewInternalPort, NewInternalClient,
|
||||
NewEnabled, NewDescription,
|
||||
NewLeaseTime } portMappingElt;
|
||||
|
||||
struct PortMapping {
|
||||
struct PortMapping * l_next; /* list next element */
|
||||
UNSIGNED_INTEGER leaseTime;
|
||||
unsigned short externalPort;
|
||||
unsigned short internalPort;
|
||||
char remoteHost[64];
|
||||
char internalClient[64];
|
||||
char description[64];
|
||||
char protocol[4];
|
||||
unsigned char enabled;
|
||||
};
|
||||
|
||||
struct PortMappingParserData {
|
||||
struct PortMapping * l_head; /* list head */
|
||||
portMappingElt curelt;
|
||||
};
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
ParsePortListing(const char * buffer, int bufsize,
|
||||
struct PortMappingParserData * pdata);
|
||||
|
||||
MINIUPNP_LIBSPEC void
|
||||
FreePortListing(struct PortMappingParserData * pdata);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
105
deps/miniupnpc/receivedata.c
vendored
Normal file
105
deps/miniupnpc/receivedata.c
vendored
Normal file
@ -0,0 +1,105 @@
|
||||
/* $Id: receivedata.c,v 1.7 2015/11/09 21:51:41 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Website : http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2011-2014 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided in this distribution. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else /* _WIN32 */
|
||||
#include <unistd.h>
|
||||
#if defined(__amigaos__) && !defined(__amigaos4__)
|
||||
#define socklen_t int
|
||||
#else /* #if defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#include <sys/select.h>
|
||||
#endif /* #else defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#if !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
#include <poll.h>
|
||||
#endif /* !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#include <errno.h>
|
||||
#define MINIUPNPC_IGNORE_EINTR
|
||||
#endif /* _WIN32 */
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PRINT_SOCKET_ERROR(x) printf("Socket error: %s, %d\n", x, WSAGetLastError());
|
||||
#else
|
||||
#define PRINT_SOCKET_ERROR(x) perror(x)
|
||||
#endif
|
||||
|
||||
#include "receivedata.h"
|
||||
|
||||
int
|
||||
receivedata(int socket,
|
||||
char * data, int length,
|
||||
int timeout, unsigned int * scope_id)
|
||||
{
|
||||
#ifdef MINIUPNPC_GET_SRC_ADDR
|
||||
struct sockaddr_storage src_addr;
|
||||
socklen_t src_addr_len = sizeof(src_addr);
|
||||
#endif /* MINIUPNPC_GET_SRC_ADDR */
|
||||
int n;
|
||||
#if !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__)
|
||||
/* using poll */
|
||||
struct pollfd fds[1]; /* for the poll */
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
do {
|
||||
#endif /* MINIUPNPC_IGNORE_EINTR */
|
||||
fds[0].fd = socket;
|
||||
fds[0].events = POLLIN;
|
||||
n = poll(fds, 1, timeout);
|
||||
#ifdef MINIUPNPC_IGNORE_EINTR
|
||||
} while(n < 0 && errno == EINTR);
|
||||
#endif /* MINIUPNPC_IGNORE_EINTR */
|
||||
if(n < 0) {
|
||||
PRINT_SOCKET_ERROR("poll");
|
||||
return -1;
|
||||
} else if(n == 0) {
|
||||
/* timeout */
|
||||
return 0;
|
||||
}
|
||||
#else /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
/* using select under _WIN32 and amigaos */
|
||||
fd_set socketSet;
|
||||
TIMEVAL timeval;
|
||||
FD_ZERO(&socketSet);
|
||||
FD_SET(socket, &socketSet);
|
||||
timeval.tv_sec = timeout / 1000;
|
||||
timeval.tv_usec = (timeout % 1000) * 1000;
|
||||
n = select(FD_SETSIZE, &socketSet, NULL, NULL, &timeval);
|
||||
if(n < 0) {
|
||||
PRINT_SOCKET_ERROR("select");
|
||||
return -1;
|
||||
} else if(n == 0) {
|
||||
return 0;
|
||||
}
|
||||
#endif /* !defined(_WIN32) && !defined(__amigaos__) && !defined(__amigaos4__) */
|
||||
#ifdef MINIUPNPC_GET_SRC_ADDR
|
||||
memset(&src_addr, 0, sizeof(src_addr));
|
||||
n = recvfrom(socket, data, length, 0,
|
||||
(struct sockaddr *)&src_addr, &src_addr_len);
|
||||
#else /* MINIUPNPC_GET_SRC_ADDR */
|
||||
n = recv(socket, data, length, 0);
|
||||
#endif /* MINIUPNPC_GET_SRC_ADDR */
|
||||
if(n<0) {
|
||||
PRINT_SOCKET_ERROR("recv");
|
||||
}
|
||||
#ifdef MINIUPNPC_GET_SRC_ADDR
|
||||
if (src_addr.ss_family == AF_INET6) {
|
||||
const struct sockaddr_in6 * src_addr6 = (struct sockaddr_in6 *)&src_addr;
|
||||
#ifdef DEBUG
|
||||
printf("scope_id=%u\n", src_addr6->sin6_scope_id);
|
||||
#endif /* DEBUG */
|
||||
if(scope_id)
|
||||
*scope_id = src_addr6->sin6_scope_id;
|
||||
}
|
||||
#endif /* MINIUPNPC_GET_SRC_ADDR */
|
||||
return n;
|
||||
}
|
||||
|
19
deps/miniupnpc/receivedata.h
vendored
Normal file
19
deps/miniupnpc/receivedata.h
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/* $Id: receivedata.h,v 1.3 2012/06/23 22:34:47 nanard Exp $ */
|
||||
/* Project: miniupnp
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* Author: Thomas Bernard
|
||||
* Copyright (c) 2011-2012 Thomas Bernard
|
||||
* This software is subjects to the conditions detailed
|
||||
* in the LICENCE file provided within this distribution */
|
||||
#ifndef RECEIVEDATA_H_INCLUDED
|
||||
#define RECEIVEDATA_H_INCLUDED
|
||||
|
||||
/* Reads data from the specified socket.
|
||||
* Returns the number of bytes read if successful, zero if no bytes were
|
||||
* read or if we timed out. Returns negative if there was an error. */
|
||||
int receivedata(int socket,
|
||||
char * data, int length,
|
||||
int timeout, unsigned int * scope_id);
|
||||
|
||||
#endif
|
||||
|
53
deps/miniupnpc/updateminiupnpcstrings.sh
vendored
Executable file
53
deps/miniupnpc/updateminiupnpcstrings.sh
vendored
Executable file
@ -0,0 +1,53 @@
|
||||
#! /bin/sh
|
||||
# $Id: updateminiupnpcstrings.sh,v 1.7 2011/01/04 11:41:53 nanard Exp $
|
||||
# project miniupnp : http://miniupnp.free.fr/
|
||||
# (c) 2009 Thomas Bernard
|
||||
|
||||
FILE=miniupnpcstrings.h
|
||||
TMPFILE=miniupnpcstrings.h.tmp
|
||||
TEMPLATE_FILE=${FILE}.in
|
||||
|
||||
# detecting the OS name and version
|
||||
OS_NAME=`uname -s`
|
||||
OS_VERSION=`uname -r`
|
||||
if [ -f /etc/debian_version ]; then
|
||||
OS_NAME=Debian
|
||||
OS_VERSION=`cat /etc/debian_version`
|
||||
fi
|
||||
# use lsb_release (Linux Standard Base) when available
|
||||
LSB_RELEASE=`which lsb_release`
|
||||
if [ 0 -eq $? -a -x "${LSB_RELEASE}" ]; then
|
||||
OS_NAME=`${LSB_RELEASE} -i -s`
|
||||
OS_VERSION=`${LSB_RELEASE} -r -s`
|
||||
case $OS_NAME in
|
||||
Debian)
|
||||
#OS_VERSION=`${LSB_RELEASE} -c -s`
|
||||
;;
|
||||
Ubuntu)
|
||||
#OS_VERSION=`${LSB_RELEASE} -c -s`
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# on AmigaOS 3, uname -r returns "unknown", so we use uname -v
|
||||
if [ "$OS_NAME" = "AmigaOS" ]; then
|
||||
if [ "$OS_VERSION" = "unknown" ]; then
|
||||
OS_VERSION=`uname -v`
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Detected OS [$OS_NAME] version [$OS_VERSION]"
|
||||
MINIUPNPC_VERSION=`cat VERSION`
|
||||
echo "MiniUPnPc version [${MINIUPNPC_VERSION}]"
|
||||
|
||||
EXPR="s|OS_STRING \".*\"|OS_STRING \"${OS_NAME}/${OS_VERSION}\"|"
|
||||
#echo $EXPR
|
||||
test -f ${FILE}.in
|
||||
echo "setting OS_STRING macro value to ${OS_NAME}/${OS_VERSION} in $FILE."
|
||||
sed -e "$EXPR" < $TEMPLATE_FILE > $TMPFILE
|
||||
|
||||
EXPR="s|MINIUPNPC_VERSION_STRING \".*\"|MINIUPNPC_VERSION_STRING \"${MINIUPNPC_VERSION}\"|"
|
||||
echo "setting MINIUPNPC_VERSION_STRING macro value to ${MINIUPNPC_VERSION} in $FILE."
|
||||
sed -e "$EXPR" < $TMPFILE > $FILE
|
||||
rm $TMPFILE
|
||||
|
1240
deps/miniupnpc/upnpcommands.c
vendored
Normal file
1240
deps/miniupnpc/upnpcommands.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
348
deps/miniupnpc/upnpcommands.h
vendored
Normal file
348
deps/miniupnpc/upnpcommands.h
vendored
Normal file
@ -0,0 +1,348 @@
|
||||
/* $Id: upnpcommands.h,v 1.30 2015/07/15 12:21:28 nanard Exp $ */
|
||||
/* Miniupnp project : http://miniupnp.free.fr/
|
||||
* Author : Thomas Bernard
|
||||
* Copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed in the
|
||||
* LICENCE file provided within this distribution */
|
||||
#ifndef UPNPCOMMANDS_H_INCLUDED
|
||||
#define UPNPCOMMANDS_H_INCLUDED
|
||||
|
||||
#include "upnpreplyparse.h"
|
||||
#include "portlistingparse.h"
|
||||
#include "miniupnpc_declspec.h"
|
||||
#include "miniupnpctypes.h"
|
||||
|
||||
/* MiniUPnPc return codes : */
|
||||
#define UPNPCOMMAND_SUCCESS (0)
|
||||
#define UPNPCOMMAND_UNKNOWN_ERROR (-1)
|
||||
#define UPNPCOMMAND_INVALID_ARGS (-2)
|
||||
#define UPNPCOMMAND_HTTP_ERROR (-3)
|
||||
#define UPNPCOMMAND_INVALID_RESPONSE (-4)
|
||||
#define UPNPCOMMAND_MEM_ALLOC_ERROR (-5)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
|
||||
UPNP_GetTotalBytesSent(const char * controlURL,
|
||||
const char * servicetype);
|
||||
|
||||
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
|
||||
UPNP_GetTotalBytesReceived(const char * controlURL,
|
||||
const char * servicetype);
|
||||
|
||||
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
|
||||
UPNP_GetTotalPacketsSent(const char * controlURL,
|
||||
const char * servicetype);
|
||||
|
||||
MINIUPNP_LIBSPEC UNSIGNED_INTEGER
|
||||
UPNP_GetTotalPacketsReceived(const char * controlURL,
|
||||
const char * servicetype);
|
||||
|
||||
/* UPNP_GetStatusInfo()
|
||||
* status and lastconnerror are 64 byte buffers
|
||||
* Return values :
|
||||
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
||||
* or a UPnP Error code */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetStatusInfo(const char * controlURL,
|
||||
const char * servicetype,
|
||||
char * status,
|
||||
unsigned int * uptime,
|
||||
char * lastconnerror);
|
||||
|
||||
/* UPNP_GetConnectionTypeInfo()
|
||||
* argument connectionType is a 64 character buffer
|
||||
* Return Values :
|
||||
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
||||
* or a UPnP Error code */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetConnectionTypeInfo(const char * controlURL,
|
||||
const char * servicetype,
|
||||
char * connectionType);
|
||||
|
||||
/* UPNP_GetExternalIPAddress() call the corresponding UPNP method.
|
||||
* if the third arg is not null the value is copied to it.
|
||||
* at least 16 bytes must be available
|
||||
*
|
||||
* Return values :
|
||||
* 0 : SUCCESS
|
||||
* NON ZERO : ERROR Either an UPnP error code or an unknown error.
|
||||
*
|
||||
* possible UPnP Errors :
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 501 Action Failed - See UPnP Device Architecture section on Control. */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetExternalIPAddress(const char * controlURL,
|
||||
const char * servicetype,
|
||||
char * extIpAdd);
|
||||
|
||||
/* UPNP_GetLinkLayerMaxBitRates()
|
||||
* call WANCommonInterfaceConfig:1#GetCommonLinkProperties
|
||||
*
|
||||
* return values :
|
||||
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
||||
* or a UPnP Error Code. */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetLinkLayerMaxBitRates(const char* controlURL,
|
||||
const char* servicetype,
|
||||
unsigned int * bitrateDown,
|
||||
unsigned int * bitrateUp);
|
||||
|
||||
/* UPNP_AddPortMapping()
|
||||
* if desc is NULL, it will be defaulted to "libminiupnpc"
|
||||
* remoteHost is usually NULL because IGD don't support it.
|
||||
*
|
||||
* Return values :
|
||||
* 0 : SUCCESS
|
||||
* NON ZERO : ERROR. Either an UPnP error code or an unknown error.
|
||||
*
|
||||
* List of possible UPnP errors for AddPortMapping :
|
||||
* errorCode errorDescription (short) - Description (long)
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 501 Action Failed - See UPnP Device Architecture section on Control.
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization and
|
||||
* the sender was not authorized.
|
||||
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
|
||||
* wild-carded
|
||||
* 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
|
||||
* 718 ConflictInMappingEntry - The port mapping entry specified conflicts
|
||||
* with a mapping assigned previously to another client
|
||||
* 724 SamePortValuesRequired - Internal and External port values
|
||||
* must be the same
|
||||
* 725 OnlyPermanentLeasesSupported - The NAT implementation only supports
|
||||
* permanent lease times on port mappings
|
||||
* 726 RemoteHostOnlySupportsWildcard - RemoteHost must be a wildcard
|
||||
* and cannot be a specific IP address or DNS name
|
||||
* 727 ExternalPortOnlySupportsWildcard - ExternalPort must be a wildcard and
|
||||
* cannot be a specific port value
|
||||
* 728 NoPortMapsAvailable - There are not enough free ports available to
|
||||
* complete port mapping.
|
||||
* 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
|
||||
* due to conflict with other mechanisms.
|
||||
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_AddPortMapping(const char * controlURL, const char * servicetype,
|
||||
const char * extPort,
|
||||
const char * inPort,
|
||||
const char * inClient,
|
||||
const char * desc,
|
||||
const char * proto,
|
||||
const char * remoteHost,
|
||||
const char * leaseDuration);
|
||||
|
||||
/* UPNP_AddAnyPortMapping()
|
||||
* if desc is NULL, it will be defaulted to "libminiupnpc"
|
||||
* remoteHost is usually NULL because IGD don't support it.
|
||||
*
|
||||
* Return values :
|
||||
* 0 : SUCCESS
|
||||
* NON ZERO : ERROR. Either an UPnP error code or an unknown error.
|
||||
*
|
||||
* List of possible UPnP errors for AddPortMapping :
|
||||
* errorCode errorDescription (short) - Description (long)
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 501 Action Failed - See UPnP Device Architecture section on Control.
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization and
|
||||
* the sender was not authorized.
|
||||
* 715 WildCardNotPermittedInSrcIP - The source IP address cannot be
|
||||
* wild-carded
|
||||
* 716 WildCardNotPermittedInExtPort - The external port cannot be wild-carded
|
||||
* 728 NoPortMapsAvailable - There are not enough free ports available to
|
||||
* complete port mapping.
|
||||
* 729 ConflictWithOtherMechanisms - Attempted port mapping is not allowed
|
||||
* due to conflict with other mechanisms.
|
||||
* 732 WildCardNotPermittedInIntPort - The internal port cannot be wild-carded
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_AddAnyPortMapping(const char * controlURL, const char * servicetype,
|
||||
const char * extPort,
|
||||
const char * inPort,
|
||||
const char * inClient,
|
||||
const char * desc,
|
||||
const char * proto,
|
||||
const char * remoteHost,
|
||||
const char * leaseDuration,
|
||||
char * reservedPort);
|
||||
|
||||
/* UPNP_DeletePortMapping()
|
||||
* Use same argument values as what was used for AddPortMapping().
|
||||
* remoteHost is usually NULL because IGD don't support it.
|
||||
* Return Values :
|
||||
* 0 : SUCCESS
|
||||
* NON ZERO : error. Either an UPnP error code or an undefined error.
|
||||
*
|
||||
* List of possible UPnP errors for DeletePortMapping :
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization
|
||||
* and the sender was not authorized.
|
||||
* 714 NoSuchEntryInArray - The specified value does not exist in the array */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_DeletePortMapping(const char * controlURL, const char * servicetype,
|
||||
const char * extPort, const char * proto,
|
||||
const char * remoteHost);
|
||||
|
||||
/* UPNP_DeletePortRangeMapping()
|
||||
* Use same argument values as what was used for AddPortMapping().
|
||||
* remoteHost is usually NULL because IGD don't support it.
|
||||
* Return Values :
|
||||
* 0 : SUCCESS
|
||||
* NON ZERO : error. Either an UPnP error code or an undefined error.
|
||||
*
|
||||
* List of possible UPnP errors for DeletePortMapping :
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization
|
||||
* and the sender was not authorized.
|
||||
* 730 PortMappingNotFound - This error message is returned if no port
|
||||
* mapping is found in the specified range.
|
||||
* 733 InconsistentParameters - NewStartPort and NewEndPort values are not consistent. */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_DeletePortMappingRange(const char * controlURL, const char * servicetype,
|
||||
const char * extPortStart, const char * extPortEnd,
|
||||
const char * proto,
|
||||
const char * manage);
|
||||
|
||||
/* UPNP_GetPortMappingNumberOfEntries()
|
||||
* not supported by all routers */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetPortMappingNumberOfEntries(const char* controlURL,
|
||||
const char* servicetype,
|
||||
unsigned int * num);
|
||||
|
||||
/* UPNP_GetSpecificPortMappingEntry()
|
||||
* retrieves an existing port mapping
|
||||
* params :
|
||||
* in extPort
|
||||
* in proto
|
||||
* in remoteHost
|
||||
* out intClient (16 bytes)
|
||||
* out intPort (6 bytes)
|
||||
* out desc (80 bytes)
|
||||
* out enabled (4 bytes)
|
||||
* out leaseDuration (16 bytes)
|
||||
*
|
||||
* return value :
|
||||
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
||||
* or a UPnP Error Code.
|
||||
*
|
||||
* List of possible UPnP errors for _GetSpecificPortMappingEntry :
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 501 Action Failed - See UPnP Device Architecture section on Control.
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization
|
||||
* and the sender was not authorized.
|
||||
* 714 NoSuchEntryInArray - The specified value does not exist in the array.
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetSpecificPortMappingEntry(const char * controlURL,
|
||||
const char * servicetype,
|
||||
const char * extPort,
|
||||
const char * proto,
|
||||
const char * remoteHost,
|
||||
char * intClient,
|
||||
char * intPort,
|
||||
char * desc,
|
||||
char * enabled,
|
||||
char * leaseDuration);
|
||||
|
||||
/* UPNP_GetGenericPortMappingEntry()
|
||||
* params :
|
||||
* in index
|
||||
* out extPort (6 bytes)
|
||||
* out intClient (16 bytes)
|
||||
* out intPort (6 bytes)
|
||||
* out protocol (4 bytes)
|
||||
* out desc (80 bytes)
|
||||
* out enabled (4 bytes)
|
||||
* out rHost (64 bytes)
|
||||
* out duration (16 bytes)
|
||||
*
|
||||
* return value :
|
||||
* UPNPCOMMAND_SUCCESS, UPNPCOMMAND_INVALID_ARGS, UPNPCOMMAND_UNKNOWN_ERROR
|
||||
* or a UPnP Error Code.
|
||||
*
|
||||
* Possible UPNP Error codes :
|
||||
* 402 Invalid Args - See UPnP Device Architecture section on Control.
|
||||
* 606 Action not authorized - The action requested REQUIRES authorization
|
||||
* and the sender was not authorized.
|
||||
* 713 SpecifiedArrayIndexInvalid - The specified array index is out of bounds
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetGenericPortMappingEntry(const char * controlURL,
|
||||
const char * servicetype,
|
||||
const char * index,
|
||||
char * extPort,
|
||||
char * intClient,
|
||||
char * intPort,
|
||||
char * protocol,
|
||||
char * desc,
|
||||
char * enabled,
|
||||
char * rHost,
|
||||
char * duration);
|
||||
|
||||
/* UPNP_GetListOfPortMappings() Available in IGD v2
|
||||
*
|
||||
*
|
||||
* Possible UPNP Error codes :
|
||||
* 606 Action not Authorized
|
||||
* 730 PortMappingNotFound - no port mapping is found in the specified range.
|
||||
* 733 InconsistantParameters - NewStartPort and NewEndPort values are not
|
||||
* consistent.
|
||||
*/
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetListOfPortMappings(const char * controlURL,
|
||||
const char * servicetype,
|
||||
const char * startPort,
|
||||
const char * endPort,
|
||||
const char * protocol,
|
||||
const char * numberOfPorts,
|
||||
struct PortMappingParserData * data);
|
||||
|
||||
/* IGD:2, functions for service WANIPv6FirewallControl:1 */
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetFirewallStatus(const char * controlURL,
|
||||
const char * servicetype,
|
||||
int * firewallEnabled,
|
||||
int * inboundPinholeAllowed);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetOutboundPinholeTimeout(const char * controlURL, const char * servicetype,
|
||||
const char * remoteHost,
|
||||
const char * remotePort,
|
||||
const char * intClient,
|
||||
const char * intPort,
|
||||
const char * proto,
|
||||
int * opTimeout);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_AddPinhole(const char * controlURL, const char * servicetype,
|
||||
const char * remoteHost,
|
||||
const char * remotePort,
|
||||
const char * intClient,
|
||||
const char * intPort,
|
||||
const char * proto,
|
||||
const char * leaseTime,
|
||||
char * uniqueID);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_UpdatePinhole(const char * controlURL, const char * servicetype,
|
||||
const char * uniqueID,
|
||||
const char * leaseTime);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_DeletePinhole(const char * controlURL, const char * servicetype, const char * uniqueID);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_CheckPinholeWorking(const char * controlURL, const char * servicetype,
|
||||
const char * uniqueID, int * isWorking);
|
||||
|
||||
MINIUPNP_LIBSPEC int
|
||||
UPNP_GetPinholePackets(const char * controlURL, const char * servicetype,
|
||||
const char * uniqueID, int * packets);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
23
deps/miniupnpc/upnpdev.c
vendored
Normal file
23
deps/miniupnpc/upnpdev.c
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
/* $Id: upnpdev.c,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#include <stdlib.h>
|
||||
#include "upnpdev.h"
|
||||
|
||||
/* freeUPNPDevlist() should be used to
|
||||
* free the chained list returned by upnpDiscover() */
|
||||
void freeUPNPDevlist(struct UPNPDev * devlist)
|
||||
{
|
||||
struct UPNPDev * next;
|
||||
while(devlist)
|
||||
{
|
||||
next = devlist->pNext;
|
||||
free(devlist);
|
||||
devlist = next;
|
||||
}
|
||||
}
|
||||
|
36
deps/miniupnpc/upnpdev.h
vendored
Normal file
36
deps/miniupnpc/upnpdev.h
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
/* $Id: upnpdev.h,v 1.1 2015/08/28 12:14:19 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Web : http://miniupnp.free.fr/
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2005-2015 Thomas Bernard
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENSE file. */
|
||||
#ifndef UPNPDEV_H_INCLUDED
|
||||
#define UPNPDEV_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct UPNPDev {
|
||||
struct UPNPDev * pNext;
|
||||
char * descURL;
|
||||
char * st;
|
||||
unsigned int scope_id;
|
||||
char * usn;
|
||||
char buffer[3];
|
||||
};
|
||||
|
||||
/* freeUPNPDevlist()
|
||||
* free list returned by upnpDiscover() */
|
||||
MINIUPNP_LIBSPEC void freeUPNPDevlist(struct UPNPDev * devlist);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* UPNPDEV_H_INCLUDED */
|
107
deps/miniupnpc/upnperrors.c
vendored
Normal file
107
deps/miniupnpc/upnperrors.c
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/* $Id: upnperrors.c,v 1.5 2011/04/10 11:19:36 nanard Exp $ */
|
||||
/* Project : miniupnp
|
||||
* Author : Thomas BERNARD
|
||||
* copyright (c) 2007 Thomas Bernard
|
||||
* All Right reserved.
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
#include <string.h>
|
||||
#include "upnperrors.h"
|
||||
#include "upnpcommands.h"
|
||||
#include "miniupnpc.h"
|
||||
|
||||
const char * strupnperror(int err)
|
||||
{
|
||||
const char * s = NULL;
|
||||
switch(err) {
|
||||
case UPNPCOMMAND_SUCCESS:
|
||||
s = "Success";
|
||||
break;
|
||||
case UPNPCOMMAND_UNKNOWN_ERROR:
|
||||
s = "Miniupnpc Unknown Error";
|
||||
break;
|
||||
case UPNPCOMMAND_INVALID_ARGS:
|
||||
s = "Miniupnpc Invalid Arguments";
|
||||
break;
|
||||
case UPNPCOMMAND_INVALID_RESPONSE:
|
||||
s = "Miniupnpc Invalid response";
|
||||
break;
|
||||
case UPNPDISCOVER_SOCKET_ERROR:
|
||||
s = "Miniupnpc Socket error";
|
||||
break;
|
||||
case UPNPDISCOVER_MEMORY_ERROR:
|
||||
s = "Miniupnpc Memory allocation error";
|
||||
break;
|
||||
case 401:
|
||||
s = "Invalid Action";
|
||||
break;
|
||||
case 402:
|
||||
s = "Invalid Args";
|
||||
break;
|
||||
case 501:
|
||||
s = "Action Failed";
|
||||
break;
|
||||
case 606:
|
||||
s = "Action not authorized";
|
||||
break;
|
||||
case 701:
|
||||
s = "PinholeSpaceExhausted";
|
||||
break;
|
||||
case 702:
|
||||
s = "FirewallDisabled";
|
||||
break;
|
||||
case 703:
|
||||
s = "InboundPinholeNotAllowed";
|
||||
break;
|
||||
case 704:
|
||||
s = "NoSuchEntry";
|
||||
break;
|
||||
case 705:
|
||||
s = "ProtocolNotSupported";
|
||||
break;
|
||||
case 706:
|
||||
s = "InternalPortWildcardingNotAllowed";
|
||||
break;
|
||||
case 707:
|
||||
s = "ProtocolWildcardingNotAllowed";
|
||||
break;
|
||||
case 708:
|
||||
s = "WildcardNotPermittedInSrcIP";
|
||||
break;
|
||||
case 709:
|
||||
s = "NoPacketSent";
|
||||
break;
|
||||
case 713:
|
||||
s = "SpecifiedArrayIndexInvalid";
|
||||
break;
|
||||
case 714:
|
||||
s = "NoSuchEntryInArray";
|
||||
break;
|
||||
case 715:
|
||||
s = "WildCardNotPermittedInSrcIP";
|
||||
break;
|
||||
case 716:
|
||||
s = "WildCardNotPermittedInExtPort";
|
||||
break;
|
||||
case 718:
|
||||
s = "ConflictInMappingEntry";
|
||||
break;
|
||||
case 724:
|
||||
s = "SamePortValuesRequired";
|
||||
break;
|
||||
case 725:
|
||||
s = "OnlyPermanentLeasesSupported";
|
||||
break;
|
||||
case 726:
|
||||
s = "RemoteHostOnlySupportsWildcard";
|
||||
break;
|
||||
case 727:
|
||||
s = "ExternalPortOnlySupportsWildcard";
|
||||
break;
|
||||
default:
|
||||
s = "UnknownError";
|
||||
break;
|
||||
}
|
||||
return s;
|
||||
}
|
26
deps/miniupnpc/upnperrors.h
vendored
Normal file
26
deps/miniupnpc/upnperrors.h
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
/* $Id: upnperrors.h,v 1.2 2008/07/02 23:31:15 nanard Exp $ */
|
||||
/* (c) 2007-2015 Thomas Bernard
|
||||
* All rights reserved.
|
||||
* MiniUPnP Project.
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* This software is subjet to the conditions detailed in the
|
||||
* provided LICENCE file. */
|
||||
#ifndef UPNPERRORS_H_INCLUDED
|
||||
#define UPNPERRORS_H_INCLUDED
|
||||
|
||||
#include "miniupnpc_declspec.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* strupnperror()
|
||||
* Return a string description of the UPnP error code
|
||||
* or NULL for undefinded errors */
|
||||
MINIUPNP_LIBSPEC const char * strupnperror(int err);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
197
deps/miniupnpc/upnpreplyparse.c
vendored
Normal file
197
deps/miniupnpc/upnpreplyparse.c
vendored
Normal file
@ -0,0 +1,197 @@
|
||||
/* $Id: upnpreplyparse.c,v 1.19 2015/07/15 10:29:11 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2015 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "upnpreplyparse.h"
|
||||
#include "minixml.h"
|
||||
|
||||
static void
|
||||
NameValueParserStartElt(void * d, const char * name, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
data->topelt = 1;
|
||||
if(l>63)
|
||||
l = 63;
|
||||
memcpy(data->curelt, name, l);
|
||||
data->curelt[l] = '\0';
|
||||
data->cdata = NULL;
|
||||
data->cdatalen = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
NameValueParserEndElt(void * d, const char * name, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
struct NameValue * nv;
|
||||
(void)name;
|
||||
(void)l;
|
||||
if(!data->topelt)
|
||||
return;
|
||||
if(strcmp(data->curelt, "NewPortListing") != 0)
|
||||
{
|
||||
int l;
|
||||
/* standard case. Limited to n chars strings */
|
||||
l = data->cdatalen;
|
||||
nv = malloc(sizeof(struct NameValue));
|
||||
if(nv == NULL)
|
||||
{
|
||||
/* malloc error */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: error allocating memory",
|
||||
"NameValueParserEndElt");
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
if(l>=(int)sizeof(nv->value))
|
||||
l = sizeof(nv->value) - 1;
|
||||
strncpy(nv->name, data->curelt, 64);
|
||||
nv->name[63] = '\0';
|
||||
if(data->cdata != NULL)
|
||||
{
|
||||
memcpy(nv->value, data->cdata, l);
|
||||
nv->value[l] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
nv->value[0] = '\0';
|
||||
}
|
||||
nv->l_next = data->l_head; /* insert in list */
|
||||
data->l_head = nv;
|
||||
}
|
||||
data->cdata = NULL;
|
||||
data->cdatalen = 0;
|
||||
data->topelt = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
NameValueParserGetData(void * d, const char * datas, int l)
|
||||
{
|
||||
struct NameValueParserData * data = (struct NameValueParserData *)d;
|
||||
if(strcmp(data->curelt, "NewPortListing") == 0)
|
||||
{
|
||||
/* specific case for NewPortListing which is a XML Document */
|
||||
data->portListing = malloc(l + 1);
|
||||
if(!data->portListing)
|
||||
{
|
||||
/* malloc error */
|
||||
#ifdef DEBUG
|
||||
fprintf(stderr, "%s: error allocating memory",
|
||||
"NameValueParserGetData");
|
||||
#endif /* DEBUG */
|
||||
return;
|
||||
}
|
||||
memcpy(data->portListing, datas, l);
|
||||
data->portListing[l] = '\0';
|
||||
data->portListingLength = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* standard case. */
|
||||
data->cdata = datas;
|
||||
data->cdatalen = l;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ParseNameValue(const char * buffer, int bufsize,
|
||||
struct NameValueParserData * data)
|
||||
{
|
||||
struct xmlparser parser;
|
||||
data->l_head = NULL;
|
||||
data->portListing = NULL;
|
||||
data->portListingLength = 0;
|
||||
/* init xmlparser object */
|
||||
parser.xmlstart = buffer;
|
||||
parser.xmlsize = bufsize;
|
||||
parser.data = data;
|
||||
parser.starteltfunc = NameValueParserStartElt;
|
||||
parser.endeltfunc = NameValueParserEndElt;
|
||||
parser.datafunc = NameValueParserGetData;
|
||||
parser.attfunc = 0;
|
||||
parsexml(&parser);
|
||||
}
|
||||
|
||||
void
|
||||
ClearNameValueList(struct NameValueParserData * pdata)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
if(pdata->portListing)
|
||||
{
|
||||
free(pdata->portListing);
|
||||
pdata->portListing = NULL;
|
||||
pdata->portListingLength = 0;
|
||||
}
|
||||
while((nv = pdata->l_head) != NULL)
|
||||
{
|
||||
pdata->l_head = nv->l_next;
|
||||
free(nv);
|
||||
}
|
||||
}
|
||||
|
||||
char *
|
||||
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||
const char * Name)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
char * p = NULL;
|
||||
for(nv = pdata->l_head;
|
||||
(nv != NULL) && (p == NULL);
|
||||
nv = nv->l_next)
|
||||
{
|
||||
if(strcmp(nv->name, Name) == 0)
|
||||
p = nv->value;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* useless now that minixml ignores namespaces by itself */
|
||||
char *
|
||||
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||
const char * Name)
|
||||
{
|
||||
struct NameValue * nv;
|
||||
char * p = NULL;
|
||||
char * pname;
|
||||
for(nv = pdata->head.lh_first;
|
||||
(nv != NULL) && (p == NULL);
|
||||
nv = nv->entries.le_next)
|
||||
{
|
||||
pname = strrchr(nv->name, ':');
|
||||
if(pname)
|
||||
pname++;
|
||||
else
|
||||
pname = nv->name;
|
||||
if(strcmp(pname, Name)==0)
|
||||
p = nv->value;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* debug all-in-one function
|
||||
* do parsing then display to stdout */
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DisplayNameValueList(char * buffer, int bufsize)
|
||||
{
|
||||
struct NameValueParserData pdata;
|
||||
struct NameValue * nv;
|
||||
ParseNameValue(buffer, bufsize, &pdata);
|
||||
for(nv = pdata.l_head;
|
||||
nv != NULL;
|
||||
nv = nv->l_next)
|
||||
{
|
||||
printf("%s = %s\n", nv->name, nv->value);
|
||||
}
|
||||
ClearNameValueList(&pdata);
|
||||
}
|
||||
#endif /* DEBUG */
|
||||
|
63
deps/miniupnpc/upnpreplyparse.h
vendored
Normal file
63
deps/miniupnpc/upnpreplyparse.h
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
/* $Id: upnpreplyparse.h,v 1.19 2014/10/27 16:33:19 nanard Exp $ */
|
||||
/* MiniUPnP project
|
||||
* http://miniupnp.free.fr/ or http://miniupnp.tuxfamily.org/
|
||||
* (c) 2006-2013 Thomas Bernard
|
||||
* This software is subject to the conditions detailed
|
||||
* in the LICENCE file provided within the distribution */
|
||||
|
||||
#ifndef UPNPREPLYPARSE_H_INCLUDED
|
||||
#define UPNPREPLYPARSE_H_INCLUDED
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
struct NameValue {
|
||||
struct NameValue * l_next;
|
||||
char name[64];
|
||||
char value[128];
|
||||
};
|
||||
|
||||
struct NameValueParserData {
|
||||
struct NameValue * l_head;
|
||||
char curelt[64];
|
||||
char * portListing;
|
||||
int portListingLength;
|
||||
int topelt;
|
||||
const char * cdata;
|
||||
int cdatalen;
|
||||
};
|
||||
|
||||
/* ParseNameValue() */
|
||||
void
|
||||
ParseNameValue(const char * buffer, int bufsize,
|
||||
struct NameValueParserData * data);
|
||||
|
||||
/* ClearNameValueList() */
|
||||
void
|
||||
ClearNameValueList(struct NameValueParserData * pdata);
|
||||
|
||||
/* GetValueFromNameValueList() */
|
||||
char *
|
||||
GetValueFromNameValueList(struct NameValueParserData * pdata,
|
||||
const char * Name);
|
||||
|
||||
#if 0
|
||||
/* GetValueFromNameValueListIgnoreNS() */
|
||||
char *
|
||||
GetValueFromNameValueListIgnoreNS(struct NameValueParserData * pdata,
|
||||
const char * Name);
|
||||
#endif
|
||||
|
||||
/* DisplayNameValueList() */
|
||||
#ifdef DEBUG
|
||||
void
|
||||
DisplayNameValueList(char * buffer, int bufsize);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -33,7 +33,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "cheevos.h"
|
||||
#include "cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
|
@ -1800,7 +1800,7 @@ static bool vulkan_create_display_surface(gfx_ctx_vulkan_data_t *vk,
|
||||
VkDisplayModePropertiesKHR *modes = NULL;
|
||||
unsigned dpy, i, j;
|
||||
uint32_t best_plane = UINT32_MAX;
|
||||
VkDisplayPlaneAlphaFlagsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
VkDisplayPlaneAlphaFlagBitsKHR alpha_mode = VK_DISPLAY_PLANE_ALPHA_OPAQUE_BIT_KHR;
|
||||
VkDisplaySurfaceCreateInfoKHR create_info = { VK_STRUCTURE_TYPE_DISPLAY_SURFACE_CREATE_INFO_KHR };
|
||||
VkDisplayModeKHR best_mode = VK_NULL_HANDLE;
|
||||
|
||||
@ -1937,9 +1937,7 @@ out:
|
||||
create_info.planeStackIndex = planes[best_plane].currentStackIndex;
|
||||
create_info.transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
|
||||
create_info.globalAlpha = 1.0f;
|
||||
/* TODO/FIXME - why is this cast needed for CXX_BUILD? Why is
|
||||
* alpha_mode not just VkDisplayPlaneAlphaFlagBitsKHR to begin with? */
|
||||
create_info.alphaMode = (VkDisplayPlaneAlphaFlagBitsKHR)alpha_mode;
|
||||
create_info.alphaMode = alpha_mode;
|
||||
create_info.imageExtent.width = *width;
|
||||
create_info.imageExtent.height = *height;
|
||||
|
||||
@ -2271,6 +2269,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR };
|
||||
VkPresentModeKHR swapchain_present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
settings_t *settings = config_get_ptr();
|
||||
VkCompositeAlphaFlagBitsKHR composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
|
||||
vkDeviceWaitIdle(vk->context.device);
|
||||
|
||||
@ -2371,6 +2370,15 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
else
|
||||
pre_transform = surface_properties.currentTransform;
|
||||
|
||||
if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR)
|
||||
composite = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR)
|
||||
composite = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR;
|
||||
else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR)
|
||||
composite = VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR;
|
||||
else if (surface_properties.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR)
|
||||
composite = VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR;
|
||||
|
||||
old_swapchain = vk->swapchain;
|
||||
|
||||
info.surface = vk->vk_surface;
|
||||
@ -2382,7 +2390,7 @@ bool vulkan_create_swapchain(gfx_ctx_vulkan_data_t *vk,
|
||||
info.imageArrayLayers = 1;
|
||||
info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||
info.preTransform = pre_transform;
|
||||
info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||
info.compositeAlpha = composite;
|
||||
info.presentMode = swapchain_present_mode;
|
||||
info.clipped = true;
|
||||
info.oldSwapchain = old_swapchain;
|
||||
|
@ -16,8 +16,6 @@
|
||||
|
||||
#include <bcm_host.h>
|
||||
|
||||
#include <rthreads/rthreads.h>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
@ -36,10 +34,6 @@ struct dispmanx_page
|
||||
/* Each page contains it's own resource handler
|
||||
* instead of pointing to in by page number */
|
||||
DISPMANX_RESOURCE_HANDLE_T resource;
|
||||
bool used;
|
||||
/* Each page has it's own mutex for
|
||||
* isolating it's used flag access. */
|
||||
slock_t *page_used_mutex;
|
||||
|
||||
/* This field will allow us to access the
|
||||
* main _dispvars struct from the vsync CB function */
|
||||
@ -57,10 +51,7 @@ struct dispmanx_surface
|
||||
struct dispmanx_page *pages;
|
||||
/* the page that's currently on screen */
|
||||
struct dispmanx_page *current_page;
|
||||
/*The page to wich we will dump the render. We need to know this
|
||||
* already when we enter the surface update function. No time to wait
|
||||
* for free pages before blitting and showing the just rendered frame! */
|
||||
struct dispmanx_page *next_page;
|
||||
bool flip_page;
|
||||
unsigned int bpp;
|
||||
|
||||
VC_RECT_T src_rect;
|
||||
@ -102,12 +93,6 @@ struct dispmanx_video
|
||||
unsigned int dispmanx_width;
|
||||
unsigned int dispmanx_height;
|
||||
|
||||
/* For threading */
|
||||
scond_t *vsync_condition;
|
||||
slock_t *vsync_cond_mutex;
|
||||
slock_t *pending_mutex;
|
||||
unsigned int pageflip_pending;
|
||||
|
||||
/* Menu */
|
||||
bool menu_active;
|
||||
|
||||
@ -129,94 +114,15 @@ struct dispmanx_video
|
||||
float aspect_ratio;
|
||||
};
|
||||
|
||||
/* If no free page is available when called, wait for a page flip. */
|
||||
static struct dispmanx_page *dispmanx_get_free_page(void *data, struct dispmanx_surface *surface)
|
||||
{
|
||||
unsigned i;
|
||||
struct dispmanx_video *_dispvars = data;
|
||||
struct dispmanx_page *page = NULL;
|
||||
|
||||
while (!page)
|
||||
{
|
||||
/* Try to find a free page */
|
||||
for (i = 0; i < surface->numpages; ++i)
|
||||
{
|
||||
if (!surface->pages[i].used)
|
||||
{
|
||||
page = (surface->pages) + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If no page is free at the moment,
|
||||
* wait until a free page is freed by vsync CB. */
|
||||
if (!page)
|
||||
{
|
||||
slock_lock(_dispvars->vsync_cond_mutex);
|
||||
scond_wait(_dispvars->vsync_condition, _dispvars->vsync_cond_mutex);
|
||||
slock_unlock(_dispvars->vsync_cond_mutex);
|
||||
}
|
||||
}
|
||||
|
||||
/* We mark the choosen page as used */
|
||||
slock_lock(page->page_used_mutex);
|
||||
page->used = true;
|
||||
slock_unlock(page->page_used_mutex);
|
||||
|
||||
return page;
|
||||
}
|
||||
|
||||
static void dispmanx_vsync_callback(DISPMANX_UPDATE_HANDLE_T u, void *data)
|
||||
{
|
||||
struct dispmanx_page *page = data;
|
||||
struct dispmanx_surface *surface = page->surface;
|
||||
|
||||
/* Marking the page as free must be done before the signaling
|
||||
* so when update_main continues (it won't continue until we signal)
|
||||
* we can chose this page as free */
|
||||
if (surface->current_page)
|
||||
{
|
||||
slock_lock(surface->current_page->page_used_mutex);
|
||||
|
||||
/* We mark as free the page that was visible until now */
|
||||
surface->current_page->used = false;
|
||||
slock_unlock(surface->current_page->page_used_mutex);
|
||||
}
|
||||
|
||||
/* The page on which we issued the flip that
|
||||
* caused this callback becomes the visible one */
|
||||
surface->current_page = page;
|
||||
|
||||
/* These two things must be isolated "atomically" to avoid getting
|
||||
* a false positive in the pending_mutex test in update_main. */
|
||||
slock_lock(page->dispvars->pending_mutex);
|
||||
|
||||
page->dispvars->pageflip_pending--;
|
||||
scond_signal(page->dispvars->vsync_condition);
|
||||
|
||||
slock_unlock(page->dispvars->pending_mutex);
|
||||
}
|
||||
|
||||
static void dispmanx_surface_free(void *data, struct dispmanx_surface **sp)
|
||||
{
|
||||
int i;
|
||||
struct dispmanx_video *_dispvars = data;
|
||||
struct dispmanx_surface *surface = *sp;
|
||||
|
||||
/* What if we run into the vsync cb code after freeing the surface?
|
||||
* We could be trying to get non-existant lock, signal non-existant condition..
|
||||
* So we wait for any pending flips to complete before freeing any surface. */
|
||||
slock_lock(_dispvars->pending_mutex);
|
||||
if (_dispvars->pageflip_pending > 0)
|
||||
scond_wait(_dispvars->vsync_condition, _dispvars->pending_mutex);
|
||||
|
||||
slock_unlock(_dispvars->pending_mutex);
|
||||
|
||||
for (i = 0; i < surface->numpages; i++)
|
||||
{
|
||||
vc_dispmanx_resource_delete(surface->pages[i].resource);
|
||||
surface->pages[i].used = false;
|
||||
slock_free(surface->pages[i].page_used_mutex);
|
||||
}
|
||||
|
||||
free(surface->pages);
|
||||
@ -260,16 +166,12 @@ static void dispmanx_surface_setup(void *data, int src_width, int src_height,
|
||||
|
||||
for (i = 0; i < surface->numpages; i++)
|
||||
{
|
||||
surface->pages[i].used = false;
|
||||
surface->pages[i].surface = surface;
|
||||
surface->pages[i].dispvars = _dispvars;
|
||||
surface->pages[i].page_used_mutex = slock_new();
|
||||
}
|
||||
|
||||
/* No need to mutex this access to the "used" member because
|
||||
* the flipping/callbacks are not still running */
|
||||
surface->next_page = &(surface->pages[0]);
|
||||
surface->next_page->used = true;
|
||||
/* We blit to page 0 first */
|
||||
surface->flip_page = 0;
|
||||
|
||||
/* The "visible" width obtained from the core pitch. We blit based on
|
||||
* the "visible" width, for cores with things between scanlines. */
|
||||
@ -325,40 +227,24 @@ static void dispmanx_surface_update_async(void *data, const void *frame,
|
||||
static void dispmanx_surface_update(void *data, const void *frame,
|
||||
struct dispmanx_surface *surface)
|
||||
{
|
||||
/* Updating is very delicate: we REALLY want to show the just rendered frame ASAP,
|
||||
* so we dump and issue flip, and then we can wait for free pages, but we don't
|
||||
* want to wait for free pages at the beggining of the update or we will be
|
||||
* adding lag! */
|
||||
|
||||
struct dispmanx_video *_dispvars = data;
|
||||
|
||||
struct dispmanx_page *page = NULL;
|
||||
|
||||
page = &surface->pages[surface->flip_page];
|
||||
|
||||
/* Frame blitting */
|
||||
vc_dispmanx_resource_write_data(surface->next_page->resource, surface->pixformat,
|
||||
vc_dispmanx_resource_write_data(page->resource, surface->pixformat,
|
||||
surface->pitch, (void*)frame, &(surface->bmp_rect));
|
||||
|
||||
/* Dispmanx doesn't support more than one pending pageflip. Doing so would overwrite
|
||||
* the page in the callback function, so we would be always freeing the same page. */
|
||||
slock_lock(_dispvars->pending_mutex);
|
||||
if (_dispvars->pageflip_pending > 0)
|
||||
scond_wait(_dispvars->vsync_condition, _dispvars->pending_mutex);
|
||||
slock_unlock(_dispvars->pending_mutex);
|
||||
|
||||
/* Issue a page flip that will be done at the next vsync. */
|
||||
_dispvars->update = vc_dispmanx_update_start(0);
|
||||
|
||||
vc_dispmanx_element_change_source(_dispvars->update, surface->element,
|
||||
surface->next_page->resource);
|
||||
page->resource);
|
||||
|
||||
vc_dispmanx_update_submit(_dispvars->update,
|
||||
dispmanx_vsync_callback, (void*)(surface->next_page));
|
||||
vc_dispmanx_update_submit_sync(_dispvars->update);
|
||||
|
||||
slock_lock(_dispvars->pending_mutex);
|
||||
_dispvars->pageflip_pending++;
|
||||
slock_unlock(_dispvars->pending_mutex);
|
||||
|
||||
/* Get the next page ready for our next surface_update re-entry.
|
||||
* It's OK to wait now that we've issued the flip to the last produced frame! */
|
||||
surface->next_page = dispmanx_get_free_page(_dispvars, surface);
|
||||
surface->flip_page = !surface->flip_page;
|
||||
}
|
||||
|
||||
/* Enable/disable bilinear filtering. */
|
||||
@ -414,7 +300,6 @@ static void *dispmanx_gfx_init(const video_info_t *video,
|
||||
|
||||
/* Setup surface parameters */
|
||||
_dispvars->vc_image_ptr = 0;
|
||||
_dispvars->pageflip_pending = 0;
|
||||
_dispvars->menu_active = false;
|
||||
_dispvars->rgb32 = video->rgb32;
|
||||
|
||||
@ -424,10 +309,7 @@ static void *dispmanx_gfx_init(const video_info_t *video,
|
||||
* before we get to gfx_frame(). */
|
||||
_dispvars->aspect_ratio = video_driver_get_aspect_ratio();
|
||||
|
||||
/* Initialize the rest of the mutexes and conditions. */
|
||||
_dispvars->vsync_condition = scond_new();
|
||||
_dispvars->vsync_cond_mutex = slock_new();
|
||||
_dispvars->pending_mutex = slock_new();
|
||||
/* Initialize the rest of video variables. */
|
||||
_dispvars->core_width = 0;
|
||||
_dispvars->core_height = 0;
|
||||
_dispvars->menu_width = 0;
|
||||
@ -695,11 +577,6 @@ static void dispmanx_gfx_free(void *data)
|
||||
vc_dispmanx_display_close(_dispvars->display);
|
||||
bcm_host_deinit();
|
||||
|
||||
/* Destroy mutexes and conditions. */
|
||||
slock_free(_dispvars->pending_mutex);
|
||||
slock_free(_dispvars->vsync_cond_mutex);
|
||||
scond_free(_dispvars->vsync_condition);
|
||||
|
||||
free(_dispvars);
|
||||
}
|
||||
|
||||
|
@ -505,6 +505,7 @@ static bool sdl2_gfx_frame(void *data, const void *frame, unsigned width,
|
||||
{
|
||||
static struct retro_perf_counter sdl_copy_frame = {0};
|
||||
|
||||
SDL_RenderClear(vid->renderer);
|
||||
sdl_refresh_input_size(vid, false, vid->video.rgb32, width, height, pitch);
|
||||
|
||||
performance_counter_init(sdl_copy_frame, "sdl_copy_frame");
|
||||
|
@ -222,14 +222,14 @@ static bool vga_gfx_frame(void *data, const void *frame,
|
||||
for (x = 0; x < VGA_WIDTH; x++)
|
||||
{
|
||||
/* scale incoming frame to fit the screen */
|
||||
unsigned scaled_x = (width / (float)VGA_WIDTH) * x;
|
||||
unsigned scaled_y = (height / (float)VGA_HEIGHT) * y;
|
||||
unsigned scaled_x = (width * x) / VGA_WIDTH;
|
||||
unsigned scaled_y = (height * y) / VGA_HEIGHT;
|
||||
unsigned short pixel = ((unsigned short*)frame_to_copy)[width * scaled_y + scaled_x];
|
||||
|
||||
/* convert RGB565 to BGR332 */
|
||||
unsigned r = (7.0f / 31.0f) * ((pixel & 0xF800) >> 11);
|
||||
unsigned g = (7.0f / 63.0f) * ((pixel & 0x07E0) >> 5);
|
||||
unsigned b = (3.0f / 31.0f) * ((pixel & 0x001F) >> 0);
|
||||
unsigned r = ((pixel & 0xF800) >> 13);
|
||||
unsigned g = ((pixel & 0x07E0) >> 8);
|
||||
unsigned b = ((pixel & 0x001F) >> 3);
|
||||
|
||||
vga_frame[VGA_WIDTH * y + x] = (b << 6) | (g << 3) | r;
|
||||
}
|
||||
@ -368,10 +368,13 @@ static void vga_set_texture_frame(void *data,
|
||||
{
|
||||
for(x = 0; x < VGA_WIDTH; x++)
|
||||
{
|
||||
unsigned short pixel = video_frame[width * y + x];
|
||||
unsigned r = (7.0f / 15.0f) * ((pixel & 0xF000) >> 12);
|
||||
unsigned g = (7.0f / 15.0f) * ((pixel & 0xF00) >> 8);
|
||||
unsigned b = (3.0f / 15.0f) * ((pixel & 0xF0) >> 4);
|
||||
/* scale incoming frame to fit the screen */
|
||||
unsigned scaled_x = (width * x) / VGA_WIDTH;
|
||||
unsigned scaled_y = (height * y) / VGA_HEIGHT;
|
||||
unsigned short pixel = video_frame[width * scaled_y + scaled_x];
|
||||
unsigned r = ((pixel & 0xF000) >> 13);
|
||||
unsigned g = ((pixel & 0xF00) >> 9);
|
||||
unsigned b = ((pixel & 0xF0) >> 6);
|
||||
vga_menu_frame[VGA_WIDTH * y + x] = (b << 6) | (g << 3) | r;
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include <libretro.h>
|
||||
|
||||
#include "video_state_python.h"
|
||||
|
||||
#include "../../configuration.h"
|
||||
#include "../../dynamic.h"
|
||||
#include "../../core.h"
|
||||
#include "../../verbosity.h"
|
||||
@ -106,9 +108,10 @@ static PyObject* py_read_vram(PyObject *self, PyObject *args)
|
||||
static PyObject *py_read_input(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned user, key, i;
|
||||
rarch_joypad_info_t joypad_info;
|
||||
const struct retro_keybind *py_binds[MAX_USERS];
|
||||
int16_t res = 0;
|
||||
settings_t *settings = config_get_ptr();
|
||||
int16_t res = 0;
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
py_binds[i] = settings->input.binds[i];
|
||||
@ -121,8 +124,12 @@ static PyObject *py_read_input(PyObject *self, PyObject *args)
|
||||
if (user > MAX_USERS || user < 1 || key >= RARCH_FIRST_META_KEY)
|
||||
return NULL;
|
||||
|
||||
joypad_info.joy_idx = settings->input.joypad_map[user - 1];
|
||||
joypad_info.auto_binds = settings->input.autoconf_binds[joypad_info.joy_idx];
|
||||
|
||||
if (!input_driver_is_libretro_input_blocked())
|
||||
res = current_input->input_state(current_input_data, py_binds,
|
||||
res = current_input->input_state(current_input_data, joypad_info,
|
||||
py_binds,
|
||||
user - 1, RETRO_DEVICE_JOYPAD, 0, key);
|
||||
return PyBool_FromLong(res);
|
||||
}
|
||||
@ -130,9 +137,10 @@ static PyObject *py_read_input(PyObject *self, PyObject *args)
|
||||
static PyObject *py_read_analog(PyObject *self, PyObject *args)
|
||||
{
|
||||
unsigned user, index, id, i;
|
||||
int16_t res = 0;
|
||||
settings_t *settings = config_get_ptr();
|
||||
rarch_joypad_info_t joypad_info;
|
||||
const struct retro_keybind *py_binds[MAX_USERS];
|
||||
int16_t res = 0;
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
for (i = 0; i < MAX_USERS; i++)
|
||||
py_binds[i] = settings->input.binds[i];
|
||||
@ -145,8 +153,12 @@ static PyObject *py_read_analog(PyObject *self, PyObject *args)
|
||||
if (user > MAX_USERS || user < 1 || index > 1 || id > 1)
|
||||
return NULL;
|
||||
|
||||
res = current_input->input_state(current_input_data, py_binds,
|
||||
user - 1, RETRO_DEVICE_ANALOG, index, id)
|
||||
joypad_info.joy_idx = settings->input.joypad_map[user - 1];
|
||||
joypad_info.auto_binds = settings->input.autoconf_binds[joypad_info.joy_idx];
|
||||
|
||||
res = current_input->input_state(current_input_data,
|
||||
joypad_info, py_binds,
|
||||
user - 1, RETRO_DEVICE_ANALOG, index, id);
|
||||
return PyFloat_FromDouble((double)res / 0x7fff);
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CG) || defined(HAVE_HLSL) || defined(HAVE_GLSL)
|
||||
#if defined(HAVE_CG) || defined(HAVE_HLSL) || defined(HAVE_GLSL) || defined(HAVE_SLANG)
|
||||
#ifndef HAVE_SHADER_MANAGER
|
||||
#define HAVE_SHADER_MANAGER
|
||||
#endif
|
||||
|
@ -133,7 +133,7 @@ ACHIEVEMENTS
|
||||
|
||||
#include "../libretro-common/formats/json/jsonsax.c"
|
||||
#include "../network/net_http_special.c"
|
||||
#include "../cheevos.c"
|
||||
#include "../cheevos/cheevos.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
@ -1128,6 +1128,22 @@ XML
|
||||
#include "../database_info.c"
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_BUILTINMINIUPNPC)
|
||||
#include "../deps/miniupnpc/igd_desc_parse.c"
|
||||
#include "../deps/miniupnpc/upnpreplyparse.c"
|
||||
#include "../deps/miniupnpc/upnpcommands.c"
|
||||
#include "../deps/miniupnpc/upnperrors.c"
|
||||
#include "../deps/miniupnpc/connecthostport.c"
|
||||
#include "../deps/miniupnpc/portlistingparse.c"
|
||||
#include "../deps/miniupnpc/receivedata.c"
|
||||
#include "../deps/miniupnpc/upnpdev.c"
|
||||
#include "../deps/miniupnpc/minissdpc.c"
|
||||
#include "../deps/miniupnpc/miniwget.c"
|
||||
#include "../deps/miniupnpc/miniupnpc.c"
|
||||
#include "../deps/miniupnpc/minixml.c"
|
||||
#include "../deps/miniupnpc/minisoap.c"
|
||||
#endif
|
||||
|
||||
/*============================================================
|
||||
HTTP SERVER
|
||||
============================================================ */
|
||||
|
@ -47,6 +47,26 @@
|
||||
#define MAX_TOUCH 16
|
||||
#define MAX_NUM_KEYBOARDS 3
|
||||
|
||||
// If using an SDK lower than 14 then add missing mouse button codes
|
||||
#if __ANDROID_API__ < 14
|
||||
enum {
|
||||
AMOTION_EVENT_BUTTON_PRIMARY = 1 << 0,
|
||||
AMOTION_EVENT_BUTTON_SECONDARY = 1 << 1,
|
||||
AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2,
|
||||
AMOTION_EVENT_BUTTON_BACK = 1 << 3,
|
||||
AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
|
||||
};
|
||||
#endif
|
||||
|
||||
// If using an SDK lower than 24 then add missing relative axis codes
|
||||
#if __ANDROID_API__ < 24
|
||||
#define AMOTION_EVENT_AXIS_RELATIVE_X 27
|
||||
#define AMOTION_EVENT_AXIS_RELATIVE_Y 28
|
||||
#endif
|
||||
|
||||
// Use this to enable/disable using the touch screen as mouse
|
||||
#define ENABLE_TOUCH_SCREEN_MOUSE 1
|
||||
|
||||
typedef struct
|
||||
{
|
||||
float x;
|
||||
@ -96,6 +116,10 @@ typedef struct android_input_data
|
||||
sensor_t accelerometer_state;
|
||||
struct input_pointer pointer[MAX_TOUCH];
|
||||
unsigned pointer_count;
|
||||
int mouse_x_delta, mouse_y_delta;
|
||||
int mouse_x_prev, mouse_y_prev;
|
||||
int mouse_l, mouse_r;
|
||||
int64_t quick_tap_time;
|
||||
} android_input_data_t;
|
||||
|
||||
typedef struct android_input
|
||||
@ -123,6 +147,12 @@ static typeof(AMotionEvent_getAxisValue) *p_AMotionEvent_getAxisValue;
|
||||
|
||||
#define AMotionEvent_getAxisValue (*p_AMotionEvent_getAxisValue)
|
||||
|
||||
extern int32_t AMotionEvent_getButtonState(const AInputEvent* motion_event);
|
||||
|
||||
static typeof(AMotionEvent_getButtonState) *p_AMotionEvent_getButtonState;
|
||||
|
||||
#define AMotionEvent_getButtonState (*p_AMotionEvent_getButtonState)
|
||||
|
||||
static void *libandroid_handle;
|
||||
|
||||
static bool android_input_lookup_name_prekitkat(char *buf,
|
||||
@ -452,6 +482,9 @@ static bool android_input_init_handle(void)
|
||||
RARCH_LOG("Set engine_handle_dpad to 'Get Axis Value' (for reading extra analog sticks)");
|
||||
engine_handle_dpad = engine_handle_dpad_getaxisvalue;
|
||||
}
|
||||
|
||||
p_AMotionEvent_getButtonState = dlsym(RTLD_DEFAULT,"AMotionEvent_getButtonState");
|
||||
|
||||
pad_id1 = -1;
|
||||
pad_id2 = -1;
|
||||
|
||||
@ -470,6 +503,8 @@ static void *android_input_init(const char *joypad_driver)
|
||||
|
||||
android->thread.pads_connected = 0;
|
||||
android->copy.pads_connected = 0;
|
||||
android->thread.quick_tap_time = 0;
|
||||
android->copy.quick_tap_time = 0;
|
||||
android->joypad = input_joypad_init_driver(joypad_driver, android);
|
||||
|
||||
input_keymaps_init_keyboard_lut(rarch_key_map_android);
|
||||
@ -495,6 +530,59 @@ static void *android_input_init(const char *joypad_driver)
|
||||
return android;
|
||||
}
|
||||
|
||||
static int16_t android_mouse_state(android_input_data_t *android_data, unsigned id)
|
||||
{
|
||||
switch (id)
|
||||
{
|
||||
case RETRO_DEVICE_ID_MOUSE_LEFT:
|
||||
return android_data->mouse_l;
|
||||
case RETRO_DEVICE_ID_MOUSE_RIGHT:
|
||||
return android_data->mouse_r;
|
||||
case RETRO_DEVICE_ID_MOUSE_X:
|
||||
return android_data->mouse_x_delta;
|
||||
case RETRO_DEVICE_ID_MOUSE_Y:
|
||||
return android_data->mouse_y_delta;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static INLINE void android_mouse_calculate_deltas(android_input_data_t *android_data, AInputEvent *event,size_t motion_ptr)
|
||||
{
|
||||
// Adjust mouse speed based on ratio between core resolution and system resolution
|
||||
float x_scale = 1;
|
||||
float y_scale = 1;
|
||||
video_viewport_t *custom_vp = video_viewport_get_custom();
|
||||
struct retro_system_av_info *av_info = video_viewport_get_system_av_info();
|
||||
if(custom_vp && av_info)
|
||||
{
|
||||
const struct retro_game_geometry *geom = (const struct retro_game_geometry*)&av_info->geometry;
|
||||
x_scale = 2 * (float)geom->base_width / (float)custom_vp->width;
|
||||
y_scale = 2 * (float)geom->base_height / (float)custom_vp->height;
|
||||
}
|
||||
|
||||
// This axis is only available on Android Nougat and on Android devices with NVIDIA extensions
|
||||
float x = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_X, motion_ptr);
|
||||
float y = AMotionEvent_getAxisValue(event,AMOTION_EVENT_AXIS_RELATIVE_Y, motion_ptr);
|
||||
|
||||
// Use AXIS_RELATIVE values if they were available
|
||||
if (x != 0 || y != 0)
|
||||
{
|
||||
android_data->mouse_x_delta = ceil(x * x_scale);
|
||||
android_data->mouse_y_delta = ceil(y * y_scale);
|
||||
}
|
||||
// If not then calculate deltas based on AXIS_X and AXIS_Y. This has limitations compared to AXIS_RELATIVE
|
||||
// because once the Android mouse cursor hits the edge of the screen it is not possible to move the in-game
|
||||
// mouse any further in that direction.
|
||||
else
|
||||
{
|
||||
android_data->mouse_x_delta = ceil((AMotionEvent_getX(event, motion_ptr) - android_data->mouse_x_prev) * x_scale);
|
||||
android_data->mouse_y_delta = ceil((AMotionEvent_getY(event, motion_ptr) - android_data->mouse_y_prev) * y_scale);
|
||||
android_data->mouse_x_prev = AMotionEvent_getX(event, motion_ptr);
|
||||
android_data->mouse_y_prev = AMotionEvent_getY(event, motion_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static INLINE int android_input_poll_event_type_motion(
|
||||
android_input_data_t *android_data, AInputEvent *event,
|
||||
int port, int source)
|
||||
@ -502,6 +590,7 @@ static INLINE int android_input_poll_event_type_motion(
|
||||
int getaction, action;
|
||||
size_t motion_ptr;
|
||||
bool keyup;
|
||||
int btn;
|
||||
|
||||
// Only handle events from a touchscreen or mouse
|
||||
if (!(source & (AINPUT_SOURCE_TOUCHSCREEN | AINPUT_SOURCE_MOUSE)))
|
||||
@ -517,8 +606,38 @@ static INLINE int android_input_poll_event_type_motion(
|
||||
(source == AINPUT_SOURCE_MOUSE &&
|
||||
action != AMOTION_EVENT_ACTION_DOWN);
|
||||
|
||||
// If source is mouse then calculate button state and mouse deltas and don't process as touchscreen event
|
||||
if (source == AINPUT_SOURCE_MOUSE)
|
||||
{
|
||||
// getButtonState requires API level 14
|
||||
if (p_AMotionEvent_getButtonState)
|
||||
{
|
||||
btn = (int)AMotionEvent_getButtonState(event);
|
||||
android_data->mouse_l = (btn & AMOTION_EVENT_BUTTON_PRIMARY);
|
||||
android_data->mouse_r = (btn & AMOTION_EVENT_BUTTON_SECONDARY);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If getButtonState is not available then treat all MotionEvent.ACTION_DOWN as left button presses
|
||||
if (action == AMOTION_EVENT_ACTION_DOWN)
|
||||
android_data->mouse_l = 1;
|
||||
if (action == AMOTION_EVENT_ACTION_UP)
|
||||
android_data->mouse_l = 0;
|
||||
}
|
||||
android_mouse_calculate_deltas(android_data,event,motion_ptr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (keyup && motion_ptr < MAX_TOUCH)
|
||||
{
|
||||
if(action == AMOTION_EVENT_ACTION_UP && ENABLE_TOUCH_SCREEN_MOUSE)
|
||||
{
|
||||
// If touchscreen was pressed for less than 200ms then register time stamp of a quick tap
|
||||
if((AMotionEvent_getEventTime(event)-AMotionEvent_getDownTime(event))/1000000 < 200)
|
||||
android_data->quick_tap_time = AMotionEvent_getEventTime(event);
|
||||
android_data->mouse_l = 0;
|
||||
}
|
||||
|
||||
memmove(android_data->pointer + motion_ptr,
|
||||
android_data->pointer + motion_ptr + 1,
|
||||
(MAX_TOUCH - motion_ptr - 1) * sizeof(struct input_pointer));
|
||||
@ -529,6 +648,25 @@ static INLINE int android_input_poll_event_type_motion(
|
||||
{
|
||||
int pointer_max = MIN(AMotionEvent_getPointerCount(event), MAX_TOUCH);
|
||||
|
||||
if(action == AMOTION_EVENT_ACTION_DOWN && ENABLE_TOUCH_SCREEN_MOUSE)
|
||||
{
|
||||
// When touch screen is pressed, set mouse previous position to current position
|
||||
// before starting to calculate mouse movement deltas.
|
||||
android_data->mouse_x_prev = AMotionEvent_getX(event, motion_ptr);
|
||||
android_data->mouse_y_prev = AMotionEvent_getY(event, motion_ptr);
|
||||
|
||||
// If another touch happened within 200ms after a quick tap then cancel the quick tap
|
||||
// and register left mouse button as being held down
|
||||
if((AMotionEvent_getEventTime(event) - android_data->quick_tap_time)/1000000 < 200)
|
||||
{
|
||||
android_data->quick_tap_time = 0;
|
||||
android_data->mouse_l = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(action == AMOTION_EVENT_ACTION_MOVE && ENABLE_TOUCH_SCREEN_MOUSE)
|
||||
android_mouse_calculate_deltas(android_data,event,motion_ptr);
|
||||
|
||||
for (motion_ptr = 0; motion_ptr < pointer_max; motion_ptr++)
|
||||
{
|
||||
struct video_viewport vp = {0};
|
||||
@ -549,6 +687,10 @@ static INLINE int android_input_poll_event_type_motion(
|
||||
}
|
||||
}
|
||||
|
||||
// If more than one pointer detected then count it as a mouse right click
|
||||
if (ENABLE_TOUCH_SCREEN_MOUSE)
|
||||
android_data->mouse_r = (android_data->pointer_count == 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1007,6 +1149,20 @@ static void android_input_poll_memcpy(void *data)
|
||||
|
||||
memcpy(&android->copy, &android->thread, sizeof(android->copy));
|
||||
|
||||
// If a quick tap timer is active then check if more than 200ms have passed without it being
|
||||
// reset by a new motionevent. If so then queue a single left mouse click. This really should
|
||||
// by done in one of the polling functions, but since android_input_state() uses a copy of
|
||||
// the inputstate being created here, it has to be done here. Same goes for the deltas resets.
|
||||
retro_time_t now = cpu_features_get_time_usec();
|
||||
if(android->thread.quick_tap_time && (now/1000 - android->thread.quick_tap_time/1000000) >= 200)
|
||||
{
|
||||
android->thread.quick_tap_time = 0;
|
||||
android->copy.mouse_l = 1;
|
||||
}
|
||||
|
||||
android->thread.mouse_x_delta = 0;
|
||||
android->thread.mouse_y_delta = 0;
|
||||
|
||||
for (i = 0; i < MAX_PADS; i++)
|
||||
{
|
||||
for (j = 0; j < 2; j++)
|
||||
@ -1128,6 +1284,8 @@ static int16_t android_input_state(void *data,
|
||||
return input_joypad_analog(android->joypad, joypad_info,
|
||||
port, idx, id, binds[port]);
|
||||
break;
|
||||
case RETRO_DEVICE_MOUSE:
|
||||
return android_mouse_state(android_data, id);
|
||||
case RETRO_DEVICE_POINTER:
|
||||
switch (id)
|
||||
{
|
||||
|
@ -14,16 +14,17 @@
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../input_driver.h"
|
||||
#include "../input_keymaps.h"
|
||||
#include "../input_joypad_driver.h"
|
||||
#include "../drivers_keyboard/keyboard_event_dos.h"
|
||||
|
||||
typedef struct dos_input
|
||||
{
|
||||
const input_device_driver_t *joypad;
|
||||
unsigned char normal_keys[256];
|
||||
unsigned char extended_keys[256];
|
||||
} dos_input_t;
|
||||
|
||||
static void dos_input_poll(void *data)
|
||||
@ -48,7 +49,10 @@ static int16_t dos_input_state(void *data,
|
||||
switch (device)
|
||||
{
|
||||
case RETRO_DEVICE_JOYPAD:
|
||||
return input_joypad_pressed(dos->joypad, joypad_info, port, binds[port], id);
|
||||
return input_joypad_pressed(dos->joypad, joypad_info, port, binds[port], id) ||
|
||||
dos_keyboard_port_input_pressed(binds[port], id);
|
||||
case RETRO_DEVICE_KEYBOARD:
|
||||
return dos_keyboard_port_input_pressed(binds[port], id);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@ -61,6 +65,8 @@ static void dos_input_free_input(void *data)
|
||||
if (dos && dos->joypad)
|
||||
dos->joypad->destroy();
|
||||
|
||||
dos_keyboard_free();
|
||||
|
||||
if (data)
|
||||
free(data);
|
||||
}
|
||||
@ -72,8 +78,12 @@ static void* dos_input_init(const char *joypad_driver)
|
||||
if (!dos)
|
||||
return NULL;
|
||||
|
||||
dos_keyboard_free();
|
||||
|
||||
dos->joypad = input_joypad_init_driver(joypad_driver, dos);
|
||||
|
||||
input_keymaps_init_keyboard_lut(rarch_key_map_dos);
|
||||
|
||||
return dos;
|
||||
}
|
||||
|
||||
@ -87,7 +97,6 @@ static uint64_t dos_input_get_capabilities(void *data)
|
||||
uint64_t caps = 0;
|
||||
|
||||
caps |= UINT64_C(1) << RETRO_DEVICE_JOYPAD;
|
||||
caps |= UINT64_C(1) << RETRO_DEVICE_KEYBOARD;
|
||||
|
||||
return caps;
|
||||
}
|
||||
|
@ -28,11 +28,63 @@
|
||||
#include "../input_config.h"
|
||||
#include "../input_driver.h"
|
||||
#include "../input_joypad_driver.h"
|
||||
#include "../input_keyboard.h"
|
||||
#include "../input_keymaps.h"
|
||||
#include <wiiu/nsyskbd.h>
|
||||
|
||||
#include "wiiu_dbg.h"
|
||||
|
||||
#define MAX_PADS 5
|
||||
|
||||
static unsigned char keyboardChannel = 0x00;
|
||||
static KBDModifier keyboardModifier = 0x00;
|
||||
static unsigned char keyboardCode = 0x00;
|
||||
static KEYState keyboardState[256] = { KBD_WIIU_NULL };
|
||||
|
||||
void kb_connection_callback(KBDKeyEvent *key) {
|
||||
keyboardChannel = keyboardChannel + (key->channel + 0x01);
|
||||
}
|
||||
|
||||
void kb_disconnection_callback(KBDKeyEvent *key) {
|
||||
keyboardChannel = keyboardChannel - (key->channel + 0x01);
|
||||
}
|
||||
|
||||
void kb_key_callback(KBDKeyEvent *key) {
|
||||
keyboardModifier = key->modifier;
|
||||
keyboardCode = key->scancode;
|
||||
|
||||
bool pressed = false;
|
||||
|
||||
if (key->state > 0)
|
||||
{
|
||||
pressed = true;
|
||||
}
|
||||
uint16_t mod = 0;
|
||||
unsigned code = input_keymaps_translate_keysym_to_rk(key->scancode);
|
||||
keyboardState[code] = key->state;
|
||||
|
||||
if (key->modifier & KBD_WIIU_SHIFT)
|
||||
mod |= RETROKMOD_SHIFT;
|
||||
|
||||
if (key->modifier & KBD_WIIU_CTRL)
|
||||
mod |= RETROKMOD_CTRL;
|
||||
|
||||
if (key->modifier & KBD_WIIU_ALT)
|
||||
mod |= RETROKMOD_ALT;
|
||||
|
||||
if (key->modifier & KBD_WIIU_NUM_LOCK)
|
||||
mod |= RETROKMOD_NUMLOCK;
|
||||
|
||||
if (key->modifier & KBD_WIIU_CAPS_LOCK)
|
||||
mod |= RETROKMOD_CAPSLOCK;
|
||||
|
||||
if (key->modifier & KBD_WIIU_SCROLL_LOCK)
|
||||
mod |= RETROKMOD_SCROLLOCK;
|
||||
|
||||
input_keyboard_event(pressed, code, (char)key->UTF16, mod,
|
||||
RETRO_DEVICE_KEYBOARD);
|
||||
}
|
||||
|
||||
typedef struct wiiu_input
|
||||
{
|
||||
bool blocked;
|
||||
@ -46,7 +98,20 @@ static void wiiu_input_poll(void *data)
|
||||
wiiu_input_t *wiiu = (wiiu_input_t*)data;
|
||||
|
||||
if (wiiu->joypad)
|
||||
wiiu->joypad->poll();
|
||||
wiiu->joypad->poll();
|
||||
}
|
||||
|
||||
static bool wiiu_key_pressed(int key)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if (key >= RETROK_LAST)
|
||||
return false;
|
||||
|
||||
if ((keyboardState[key] > 0) && (keyboardChannel > 0))
|
||||
ret = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int16_t wiiu_input_state(void *data,
|
||||
@ -59,11 +124,13 @@ static int16_t wiiu_input_state(void *data,
|
||||
|
||||
if(!wiiu || !(port < MAX_PADS) || !binds || !binds[port])
|
||||
return 0;
|
||||
|
||||
|
||||
switch (device)
|
||||
{
|
||||
case RETRO_DEVICE_JOYPAD:
|
||||
return input_joypad_pressed(wiiu->joypad, joypad_info, port, binds[port], id);
|
||||
case RETRO_DEVICE_KEYBOARD:
|
||||
return wiiu_key_pressed(id);
|
||||
case RETRO_DEVICE_ANALOG:
|
||||
if (binds[port])
|
||||
return input_joypad_analog(wiiu->joypad, joypad_info, port, idx, id, binds[port]);
|
||||
@ -79,6 +146,8 @@ static void wiiu_input_free_input(void *data)
|
||||
|
||||
if (wiiu && wiiu->joypad)
|
||||
wiiu->joypad->destroy();
|
||||
|
||||
KBDTeardown();
|
||||
|
||||
free(data);
|
||||
}
|
||||
@ -91,6 +160,10 @@ static void* wiiu_input_init(const char *joypad_driver)
|
||||
|
||||
DEBUG_STR(joypad_driver);
|
||||
wiiu->joypad = input_joypad_init_driver(joypad_driver, wiiu);
|
||||
|
||||
KBDSetup(&kb_connection_callback,&kb_disconnection_callback,&kb_key_callback);
|
||||
|
||||
input_keymaps_init_keyboard_lut(rarch_key_map_wiiu);
|
||||
|
||||
return wiiu;
|
||||
}
|
||||
@ -107,7 +180,7 @@ static uint64_t wiiu_input_get_capabilities(void *data)
|
||||
{
|
||||
(void)data;
|
||||
|
||||
return (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG);
|
||||
return (1 << RETRO_DEVICE_JOYPAD) | (1 << RETRO_DEVICE_ANALOG) | (1 << RETRO_DEVICE_KEYBOARD);
|
||||
}
|
||||
|
||||
static const input_device_driver_t *wiiu_input_get_joypad_driver(void *data)
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <dpmi.h>
|
||||
#include <sys/segments.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@ -30,19 +29,17 @@
|
||||
#include "../input_joypad_driver.h"
|
||||
#include "../input_driver.h"
|
||||
#include "../input_config.h"
|
||||
#include "../input_keyboard.h"
|
||||
#include "../input_keymaps.h"
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
|
||||
#define MAX_PADS 1
|
||||
#include "../drivers_keyboard/keyboard_event_dos.h"
|
||||
|
||||
#define END_FUNC(x) static void x##_End() { }
|
||||
|
||||
#define LOCK_VAR(x) LockData((void*)&x, sizeof(x))
|
||||
#define LOCK_FUNC(x) LockCode(x, (int)x##_End - (int)x)
|
||||
|
||||
static uint64_t dos_key_state[MAX_PADS];
|
||||
|
||||
static unsigned char normal_keys[256];
|
||||
static unsigned char extended_keys[256];
|
||||
static uint16_t normal_keys[LAST_KEYCODE + 1];
|
||||
|
||||
static _go32_dpmi_seginfo old_kbd_int;
|
||||
static _go32_dpmi_seginfo kbd_int;
|
||||
@ -85,7 +82,7 @@ int LockCode(void *a, int size)
|
||||
|
||||
static void keyb_int(void)
|
||||
{
|
||||
static unsigned char buffer;
|
||||
static unsigned char buffer = 0;
|
||||
unsigned char rawcode;
|
||||
unsigned char make_break;
|
||||
int scancode;
|
||||
@ -99,14 +96,14 @@ static void keyb_int(void)
|
||||
/* second byte of an extended key */
|
||||
if (scancode < 0x60)
|
||||
{
|
||||
extended_keys[scancode] = make_break;
|
||||
normal_keys[scancode | (1 << 8)] = make_break;
|
||||
}
|
||||
|
||||
buffer = 0;
|
||||
}
|
||||
else if (buffer >= 0xE1 && buffer <= 0xE2)
|
||||
{
|
||||
buffer = 0; /* ingore these extended keys */
|
||||
buffer = 0; /* ignore these extended keys */
|
||||
}
|
||||
else if (rawcode >= 0xE0 && rawcode <= 0xE2)
|
||||
{
|
||||
@ -126,10 +123,10 @@ static void hook_keyb_int(void)
|
||||
_go32_dpmi_get_protected_mode_interrupt_vector(9, &old_kbd_int);
|
||||
|
||||
memset(&kbd_int, 0, sizeof(kbd_int));
|
||||
memset(normal_keys, 0, sizeof(normal_keys));
|
||||
|
||||
LOCK_FUNC(keyb_int);
|
||||
LOCK_VAR(normal_keys);
|
||||
LOCK_VAR(extended_keys);
|
||||
|
||||
kbd_int.pm_selector = _go32_my_cs();
|
||||
kbd_int.pm_offset = (uint32_t)&keyb_int;
|
||||
@ -170,8 +167,6 @@ static void dos_joypad_autodetect_add(unsigned autoconf_pad)
|
||||
|
||||
static bool dos_joypad_init(void *data)
|
||||
{
|
||||
memset(dos_key_state, 0, sizeof(dos_key_state));
|
||||
|
||||
hook_keyb_int();
|
||||
|
||||
dos_joypad_autodetect_add(0);
|
||||
@ -183,44 +178,64 @@ static bool dos_joypad_init(void *data)
|
||||
|
||||
static bool dos_joypad_button(unsigned port_num, uint16_t key)
|
||||
{
|
||||
uint16_t *buf = dos_keyboard_state_get(port_num);
|
||||
|
||||
if (port_num >= MAX_PADS)
|
||||
return false;
|
||||
|
||||
return (dos_key_state[port_num] & (UINT64_C(1) << key));
|
||||
}
|
||||
switch (key)
|
||||
{
|
||||
case RETRO_DEVICE_ID_JOYPAD_A:
|
||||
return buf[DOSKEY_x];
|
||||
case RETRO_DEVICE_ID_JOYPAD_B:
|
||||
return buf[DOSKEY_z];
|
||||
case RETRO_DEVICE_ID_JOYPAD_X:
|
||||
return buf[DOSKEY_s];
|
||||
case RETRO_DEVICE_ID_JOYPAD_Y:
|
||||
return buf[DOSKEY_a];
|
||||
case RETRO_DEVICE_ID_JOYPAD_SELECT:
|
||||
return buf[DOSKEY_RSHIFT];
|
||||
case RETRO_DEVICE_ID_JOYPAD_START:
|
||||
return buf[DOSKEY_RETURN];
|
||||
case RETRO_DEVICE_ID_JOYPAD_UP:
|
||||
return buf[DOSKEY_UP];
|
||||
case RETRO_DEVICE_ID_JOYPAD_DOWN:
|
||||
return buf[DOSKEY_DOWN];
|
||||
case RETRO_DEVICE_ID_JOYPAD_LEFT:
|
||||
return buf[DOSKEY_LEFT];
|
||||
case RETRO_DEVICE_ID_JOYPAD_RIGHT:
|
||||
return buf[DOSKEY_RIGHT];
|
||||
}
|
||||
|
||||
static uint64_t dos_joypad_get_buttons(unsigned port_num)
|
||||
{
|
||||
if (port_num >= MAX_PADS)
|
||||
return 0;
|
||||
return dos_key_state[port_num];
|
||||
return false;
|
||||
}
|
||||
|
||||
static void dos_joypad_poll(void)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
for (i = 0; i < MAX_PADS; i++)
|
||||
for (i = 0; i <= MAX_PADS; i++)
|
||||
{
|
||||
uint64_t *cur_state = &dos_key_state[i];
|
||||
uint16_t *cur_state = dos_keyboard_state_get(i);
|
||||
uint32_t key;
|
||||
|
||||
*cur_state = 0;
|
||||
*cur_state |= extended_keys[75] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_LEFT : 0;
|
||||
*cur_state |= extended_keys[77] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_RIGHT : 0;
|
||||
*cur_state |= extended_keys[72] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_UP : 0;
|
||||
*cur_state |= extended_keys[80] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_DOWN : 0;
|
||||
*cur_state |= normal_keys[28] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_START : 0; /* ENTER */
|
||||
*cur_state |= normal_keys[54] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_SELECT : 0; /* RSHIFT */
|
||||
*cur_state |= normal_keys[44] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_B : 0; /* Z */
|
||||
*cur_state |= normal_keys[45] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_A : 0; /* X */
|
||||
*cur_state |= normal_keys[30] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_Y : 0; /* A */
|
||||
*cur_state |= normal_keys[31] ? UINT64_C(1) << RETRO_DEVICE_ID_JOYPAD_X : 0; /* S */
|
||||
for (key = 0; key < LAST_KEYCODE; key++)
|
||||
{
|
||||
if (cur_state[key] != normal_keys[key])
|
||||
{
|
||||
unsigned code = input_keymaps_translate_keysym_to_rk(key);
|
||||
|
||||
input_keyboard_event(normal_keys[key], code, code, 0, RETRO_DEVICE_KEYBOARD);
|
||||
}
|
||||
}
|
||||
|
||||
memcpy(cur_state, normal_keys, sizeof(normal_keys));
|
||||
}
|
||||
}
|
||||
|
||||
static bool dos_joypad_query_pad(unsigned pad)
|
||||
{
|
||||
return pad < MAX_USERS && dos_key_state[pad];
|
||||
return (pad < MAX_USERS);
|
||||
}
|
||||
|
||||
static int16_t dos_joypad_axis(unsigned port_num, uint32_t joyaxis)
|
||||
@ -238,7 +253,7 @@ input_device_driver_t dos_joypad = {
|
||||
dos_joypad_query_pad,
|
||||
dos_joypad_destroy,
|
||||
dos_joypad_button,
|
||||
dos_joypad_get_buttons,
|
||||
NULL,
|
||||
dos_joypad_axis,
|
||||
dos_joypad_poll,
|
||||
NULL,
|
||||
|
56
input/drivers_keyboard/keyboard_event_dos.c
Normal file
56
input/drivers_keyboard/keyboard_event_dos.c
Normal file
@ -0,0 +1,56 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2017 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <retro_miscellaneous.h>
|
||||
|
||||
#include "keyboard_event_dos.h"
|
||||
#include "../input_keymaps.h"
|
||||
|
||||
#define MAX_KEYS LAST_KEYCODE + 1
|
||||
|
||||
// First ports are used to keeping track of gamepad states. Last port is used for keyboard state
|
||||
static uint16_t dos_key_state[MAX_PADS+1][MAX_KEYS];
|
||||
|
||||
bool dos_keyboard_port_input_pressed(const struct retro_keybind *binds, unsigned id)
|
||||
{
|
||||
if (id < RARCH_BIND_LIST_END)
|
||||
{
|
||||
const struct retro_keybind *bind = &binds[id];
|
||||
unsigned key = input_keymaps_translate_rk_to_keysym(bind->key);
|
||||
return dos_key_state[DOS_KEYBOARD_PORT][key];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dos_keyboard_input_pressed(unsigned key)
|
||||
{
|
||||
unsigned keysym = input_keymaps_translate_rk_to_keysym(key);
|
||||
return dos_key_state[DOS_KEYBOARD_PORT][keysym];
|
||||
}
|
||||
|
||||
uint16_t *dos_keyboard_state_get(unsigned port)
|
||||
{
|
||||
return dos_key_state[port];
|
||||
}
|
||||
|
||||
void dos_keyboard_free(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = 0; i < MAX_PADS; i++)
|
||||
for (j = 0; j < MAX_KEYS; j++)
|
||||
dos_key_state[i][j] = 0;
|
||||
}
|
137
input/drivers_keyboard/keyboard_event_dos.h
Normal file
137
input/drivers_keyboard/keyboard_event_dos.h
Normal file
@ -0,0 +1,137 @@
|
||||
/* RetroArch - A frontend for libretro.
|
||||
* Copyright (C) 2011-2017 - Daniel De Matteis
|
||||
* Copyright (C) 2016-2017 - Brad Parker
|
||||
*
|
||||
* RetroArch is free software: you can redistribute it and/or modify it under the terms
|
||||
* of the GNU General Public License as published by the Free Software Found-
|
||||
* ation, either version 3 of the License, or (at your option) any later version.
|
||||
*
|
||||
* RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
|
||||
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
|
||||
* PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with RetroArch.
|
||||
* If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef _KEYBOARD_EVENT_DOS_H
|
||||
#define _KEYBOARD_EVENT_DOS_H
|
||||
|
||||
#include "../input_driver.h"
|
||||
|
||||
/*
|
||||
* Key codes.
|
||||
*/
|
||||
enum {
|
||||
DOSKEY_ESCAPE = 0x1,
|
||||
DOSKEY_F1 = 0x3b,
|
||||
DOSKEY_F2 = 0x3c,
|
||||
DOSKEY_F3 = 0x3d,
|
||||
DOSKEY_F4 = 0x3e,
|
||||
DOSKEY_F5 = 0x3f,
|
||||
DOSKEY_F6 = 0x40,
|
||||
DOSKEY_F7 = 0x41,
|
||||
DOSKEY_F8 = 0x42,
|
||||
DOSKEY_F9 = 0x43,
|
||||
DOSKEY_F10 = 0x44,
|
||||
|
||||
DOSKEY_BACKQUOTE = 0x29,
|
||||
DOSKEY_1 = 0x2,
|
||||
DOSKEY_2 = 0x3,
|
||||
DOSKEY_3 = 0x4,
|
||||
DOSKEY_4 = 0x5,
|
||||
DOSKEY_5 = 0x6,
|
||||
DOSKEY_6 = 0x7,
|
||||
DOSKEY_7 = 0x8,
|
||||
DOSKEY_8 = 0x9,
|
||||
DOSKEY_9 = 0xa,
|
||||
DOSKEY_0 = 0xb,
|
||||
DOSKEY_MINUS = 0xc,
|
||||
DOSKEY_EQUAL = 0xd,
|
||||
DOSKEY_BACKSPACE = 0xe,
|
||||
|
||||
DOSKEY_TAB = 0xf,
|
||||
DOSKEY_q = 0x10,
|
||||
DOSKEY_w = 0x11,
|
||||
DOSKEY_e = 0x12,
|
||||
DOSKEY_r = 0x13,
|
||||
DOSKEY_t = 0x14,
|
||||
DOSKEY_y = 0x15,
|
||||
DOSKEY_u = 0x16,
|
||||
DOSKEY_i = 0x17,
|
||||
DOSKEY_o = 0x18,
|
||||
DOSKEY_p = 0x19,
|
||||
DOSKEY_LBRACKET = 0x1a,
|
||||
DOSKEY_RBRACKET = 0x1b,
|
||||
DOSKEY_BACKSLASH = 0x2b,
|
||||
|
||||
DOSKEY_CAPSLOCK = 0x3a,
|
||||
DOSKEY_a = 0x1e,
|
||||
DOSKEY_s = 0x1f,
|
||||
DOSKEY_d = 0x20,
|
||||
DOSKEY_f = 0x21,
|
||||
DOSKEY_g = 0x22,
|
||||
DOSKEY_h = 0x23,
|
||||
DOSKEY_j = 0x24,
|
||||
DOSKEY_k = 0x25,
|
||||
DOSKEY_l = 0x26,
|
||||
DOSKEY_SEMICOLON = 0x27,
|
||||
DOSKEY_QUOTE = 0x28,
|
||||
DOSKEY_RETURN = 0x1c,
|
||||
|
||||
DOSKEY_LSHIFT = 0x2a,
|
||||
DOSKEY_z = 0x2c,
|
||||
DOSKEY_x = 0x2d,
|
||||
DOSKEY_c = 0x2e,
|
||||
DOSKEY_v = 0x2f,
|
||||
DOSKEY_b = 0x30,
|
||||
DOSKEY_n = 0x31,
|
||||
DOSKEY_m = 0x32,
|
||||
DOSKEY_COMMA = 0x33,
|
||||
DOSKEY_PERIOD = 0x34,
|
||||
DOSKEY_SLASH = 0x35,
|
||||
DOSKEY_RSHIFT = 0x36,
|
||||
|
||||
DOSKEY_LCTRL = 0x1d,
|
||||
DOSKEY_LSUPER = 0x15b,
|
||||
DOSKEY_LALT = 0x38,
|
||||
DOSKEY_SPACE = 0x39,
|
||||
DOSKEY_RALT = 0x138,
|
||||
DOSKEY_RSUPER = 0x15c,
|
||||
DOSKEY_MENU = 0x15d,
|
||||
DOSKEY_RCTRL = 0x11d,
|
||||
|
||||
DOSKEY_UP = 0x148,
|
||||
DOSKEY_DOWN = 0x150,
|
||||
DOSKEY_LEFT = 0x14b,
|
||||
DOSKEY_RIGHT = 0x14d,
|
||||
|
||||
DOSKEY_HOME = 0x147,
|
||||
DOSKEY_END = 0x14f,
|
||||
DOSKEY_PGUP = 0x149,
|
||||
DOSKEY_PGDN = 0x151,
|
||||
};
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boolean.h>
|
||||
|
||||
#define LAST_KEYCODE 0x1ff
|
||||
|
||||
#ifndef MAX_PADS
|
||||
#define MAX_PADS 1
|
||||
#endif
|
||||
|
||||
#define DOS_KEYBOARD_PORT MAX_PADS
|
||||
|
||||
bool dos_keyboard_port_input_pressed(const struct retro_keybind *binds, unsigned id);
|
||||
|
||||
bool dos_keyboard_input_pressed(unsigned key);
|
||||
|
||||
uint16_t *dos_keyboard_state_get(unsigned port);
|
||||
|
||||
void dos_keyboard_init(void);
|
||||
|
||||
void dos_keyboard_free(void);
|
||||
|
||||
#endif
|
@ -606,6 +606,7 @@ static INLINE bool input_menu_keys_pressed_internal(
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
static bool input_driver_toggle_button_combo(
|
||||
unsigned mode, uint64_t *trigger_input)
|
||||
{
|
||||
@ -650,6 +651,7 @@ static bool input_driver_toggle_button_combo(
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* input_menu_keys_pressed:
|
||||
@ -723,6 +725,7 @@ uint64_t input_menu_keys_pressed(
|
||||
input_driver_block_hotkey = true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
if ( ((settings->input.menu_toggle_gamepad_combo != INPUT_TOGGLE_NONE) &&
|
||||
input_driver_toggle_button_combo(
|
||||
settings->input.menu_toggle_gamepad_combo, &old_input))
|
||||
@ -731,6 +734,7 @@ uint64_t input_menu_keys_pressed(
|
||||
settings->input.binds[0][RARCH_MENU_TOGGLE].valid,
|
||||
settings->input.all_users_control_menu))
|
||||
ret |= (UINT64_C(1) << RARCH_MENU_TOGGLE);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < RARCH_BIND_LIST_END; i++)
|
||||
{
|
||||
@ -944,11 +948,13 @@ uint64_t input_keys_pressed(
|
||||
input_driver_block_hotkey = false;
|
||||
}
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
if (
|
||||
((settings->input.menu_toggle_gamepad_combo != INPUT_TOGGLE_NONE) &&
|
||||
input_driver_toggle_button_combo(settings->input.menu_toggle_gamepad_combo, &old_input))
|
||||
|| input_keys_pressed_internal(settings, joypad_info, RARCH_MENU_TOGGLE, binds))
|
||||
ret |= (UINT64_C(1) << RARCH_MENU_TOGGLE);
|
||||
#endif
|
||||
|
||||
for (i = 0; i < RARCH_BIND_LIST_END; i++)
|
||||
{
|
||||
|
@ -32,6 +32,10 @@
|
||||
#include "drivers_keyboard/keyboard_event_android.h"
|
||||
#endif
|
||||
|
||||
#ifdef DJGPP
|
||||
#include "drivers_keyboard/keyboard_event_dos.h"
|
||||
#endif
|
||||
|
||||
#ifdef __QNX__
|
||||
#include <sys/keycodes.h>
|
||||
#endif
|
||||
@ -680,6 +684,112 @@ const struct rarch_key_map rarch_key_map_rwebinput[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef WIIU
|
||||
const struct rarch_key_map rarch_key_map_wiiu[] = {
|
||||
{ 4, RETROK_a },
|
||||
{ 5, RETROK_b },
|
||||
{ 6, RETROK_c },
|
||||
{ 7, RETROK_d },
|
||||
{ 8, RETROK_e },
|
||||
{ 9, RETROK_f },
|
||||
{ 10, RETROK_g },
|
||||
{ 11, RETROK_h },
|
||||
{ 12, RETROK_i },
|
||||
{ 13, RETROK_j },
|
||||
{ 14, RETROK_k },
|
||||
{ 15, RETROK_l },
|
||||
{ 16, RETROK_m },
|
||||
{ 17, RETROK_n },
|
||||
{ 18, RETROK_o },
|
||||
{ 19, RETROK_p },
|
||||
{ 20, RETROK_q },
|
||||
{ 21, RETROK_r },
|
||||
{ 22, RETROK_s },
|
||||
{ 23, RETROK_t },
|
||||
{ 24, RETROK_u },
|
||||
{ 25, RETROK_v },
|
||||
{ 26, RETROK_w },
|
||||
{ 27, RETROK_x },
|
||||
{ 28, RETROK_y },
|
||||
{ 29, RETROK_z },
|
||||
{ 30, RETROK_1 },
|
||||
{ 31, RETROK_2 },
|
||||
{ 32, RETROK_3 },
|
||||
{ 33, RETROK_4 },
|
||||
{ 34, RETROK_5 },
|
||||
{ 35, RETROK_6 },
|
||||
{ 36, RETROK_7 },
|
||||
{ 37, RETROK_8 },
|
||||
{ 38, RETROK_9 },
|
||||
{ 39, RETROK_0 },
|
||||
{ 40, RETROK_RETURN },
|
||||
{ 41, RETROK_ESCAPE },
|
||||
{ 42, RETROK_BACKSPACE },
|
||||
{ 43, RETROK_TAB },
|
||||
{ 44, RETROK_SPACE },
|
||||
{ 45, RETROK_MINUS },
|
||||
{ 46, RETROK_EQUALS },
|
||||
{ 47, RETROK_LEFTBRACKET },
|
||||
{ 48, RETROK_RIGHTBRACKET },
|
||||
{ 49, RETROK_BACKSLASH },
|
||||
{ 51, RETROK_SEMICOLON },
|
||||
{ 52, RETROK_QUOTE },
|
||||
{ 53, RETROK_BACKQUOTE },
|
||||
{ 54, RETROK_COMMA },
|
||||
{ 55, RETROK_PERIOD },
|
||||
{ 56, RETROK_SLASH },
|
||||
{ 57, RETROK_CAPSLOCK },
|
||||
{ 58, RETROK_F1 },
|
||||
{ 59, RETROK_F2 },
|
||||
{ 60, RETROK_F3 },
|
||||
{ 61, RETROK_F4 },
|
||||
{ 62, RETROK_F5 },
|
||||
{ 63, RETROK_F6 },
|
||||
{ 64, RETROK_F7 },
|
||||
{ 65, RETROK_F8 },
|
||||
{ 66, RETROK_F9 },
|
||||
{ 67, RETROK_F10 },
|
||||
{ 68, RETROK_F11 },
|
||||
{ 69, RETROK_F12 },
|
||||
{ 71, RETROK_SCROLLOCK },
|
||||
{ 72, RETROK_PAUSE },
|
||||
{ 73, RETROK_INSERT },
|
||||
{ 74, RETROK_HOME },
|
||||
{ 75, RETROK_PAGEUP },
|
||||
{ 76, RETROK_DELETE },
|
||||
{ 77, RETROK_END },
|
||||
{ 78, RETROK_PAGEDOWN },
|
||||
{ 79, RETROK_RIGHT },
|
||||
{ 80, RETROK_LEFT },
|
||||
{ 81, RETROK_DOWN },
|
||||
{ 82, RETROK_UP },
|
||||
{ 83, RETROK_NUMLOCK },
|
||||
{ 84, RETROK_KP_DIVIDE },
|
||||
{ 85, RETROK_KP_MULTIPLY },
|
||||
{ 86, RETROK_KP_MINUS },
|
||||
{ 87, RETROK_KP_PLUS },
|
||||
{ 88, RETROK_KP_ENTER },
|
||||
{ 89, RETROK_KP1 },
|
||||
{ 90, RETROK_KP2 },
|
||||
{ 91, RETROK_KP3 },
|
||||
{ 92, RETROK_KP4 },
|
||||
{ 93, RETROK_KP5 },
|
||||
{ 94, RETROK_KP6 },
|
||||
{ 95, RETROK_KP7 },
|
||||
{ 96, RETROK_KP8 },
|
||||
{ 97, RETROK_KP9 },
|
||||
{ 98, RETROK_KP0 },
|
||||
{ 99, RETROK_KP_PERIOD },
|
||||
{ 224, RETROK_LCTRL },
|
||||
{ 225, RETROK_LSHIFT },
|
||||
{ 226, RETROK_LALT },
|
||||
{ 228, RETROK_RCTRL },
|
||||
{ 229, RETROK_RSHIFT },
|
||||
{ 230, RETROK_RALT },
|
||||
{ 0, RETROK_UNKNOWN },
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_X11
|
||||
|
||||
#ifndef XF68XK_Calculator
|
||||
@ -1311,6 +1421,100 @@ const struct rarch_key_map rarch_key_map_apple_hid[] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef DJGPP
|
||||
const struct rarch_key_map rarch_key_map_dos[] = {
|
||||
{ DOSKEY_ESCAPE, RETROK_ESCAPE },
|
||||
{ DOSKEY_F1, RETROK_F1 },
|
||||
{ DOSKEY_F2, RETROK_F2 },
|
||||
{ DOSKEY_F3, RETROK_F3 },
|
||||
{ DOSKEY_F4, RETROK_F4 },
|
||||
{ DOSKEY_F5, RETROK_F5 },
|
||||
{ DOSKEY_F6, RETROK_F6 },
|
||||
{ DOSKEY_F7, RETROK_F7 },
|
||||
{ DOSKEY_F8, RETROK_F8 },
|
||||
{ DOSKEY_F9, RETROK_F9 },
|
||||
{ DOSKEY_F10, RETROK_F10 },
|
||||
|
||||
{ DOSKEY_BACKQUOTE, RETROK_BACKQUOTE },
|
||||
{ DOSKEY_1, RETROK_1 },
|
||||
{ DOSKEY_2, RETROK_2 },
|
||||
{ DOSKEY_3, RETROK_3 },
|
||||
{ DOSKEY_4, RETROK_4 },
|
||||
{ DOSKEY_5, RETROK_5 },
|
||||
{ DOSKEY_6, RETROK_6 },
|
||||
{ DOSKEY_7, RETROK_7 },
|
||||
{ DOSKEY_8, RETROK_8 },
|
||||
{ DOSKEY_9, RETROK_9 },
|
||||
{ DOSKEY_0, RETROK_0 },
|
||||
{ DOSKEY_MINUS, RETROK_MINUS },
|
||||
{ DOSKEY_EQUAL, RETROK_EQUALS },
|
||||
{ DOSKEY_BACKSPACE, RETROK_BACKSPACE },
|
||||
|
||||
{ DOSKEY_TAB, RETROK_TAB },
|
||||
{ DOSKEY_q, RETROK_q },
|
||||
{ DOSKEY_w, RETROK_w },
|
||||
{ DOSKEY_e, RETROK_e },
|
||||
{ DOSKEY_r, RETROK_r },
|
||||
{ DOSKEY_t, RETROK_t },
|
||||
{ DOSKEY_y, RETROK_y },
|
||||
{ DOSKEY_u, RETROK_u },
|
||||
{ DOSKEY_i, RETROK_i },
|
||||
{ DOSKEY_o, RETROK_o },
|
||||
{ DOSKEY_p, RETROK_p },
|
||||
{ DOSKEY_LBRACKET, RETROK_LEFTBRACKET },
|
||||
{ DOSKEY_RBRACKET, RETROK_RIGHTBRACKET },
|
||||
{ DOSKEY_BACKSLASH, RETROK_BACKSLASH },
|
||||
|
||||
{ DOSKEY_CAPSLOCK, RETROK_CAPSLOCK },
|
||||
{ DOSKEY_a, RETROK_a },
|
||||
{ DOSKEY_s, RETROK_s },
|
||||
{ DOSKEY_d, RETROK_d },
|
||||
{ DOSKEY_f, RETROK_f },
|
||||
{ DOSKEY_g, RETROK_g },
|
||||
{ DOSKEY_h, RETROK_h },
|
||||
{ DOSKEY_j, RETROK_j },
|
||||
{ DOSKEY_k, RETROK_k },
|
||||
{ DOSKEY_l, RETROK_l },
|
||||
{ DOSKEY_SEMICOLON, RETROK_SEMICOLON },
|
||||
{ DOSKEY_QUOTE, RETROK_QUOTE },
|
||||
{ DOSKEY_RETURN, RETROK_RETURN },
|
||||
|
||||
{ DOSKEY_LSHIFT, RETROK_LSHIFT },
|
||||
{ DOSKEY_z, RETROK_z },
|
||||
{ DOSKEY_x, RETROK_x },
|
||||
{ DOSKEY_c, RETROK_c },
|
||||
{ DOSKEY_v, RETROK_v },
|
||||
{ DOSKEY_b, RETROK_b },
|
||||
{ DOSKEY_n, RETROK_n },
|
||||
{ DOSKEY_m, RETROK_m },
|
||||
{ DOSKEY_COMMA, RETROK_COMMA },
|
||||
{ DOSKEY_PERIOD, RETROK_PERIOD },
|
||||
{ DOSKEY_SLASH, RETROK_SLASH },
|
||||
{ DOSKEY_RSHIFT, RETROK_RSHIFT },
|
||||
|
||||
{ DOSKEY_LCTRL, RETROK_LCTRL },
|
||||
{ DOSKEY_LSUPER, RETROK_LSUPER },
|
||||
{ DOSKEY_LALT, RETROK_LALT },
|
||||
{ DOSKEY_SPACE, RETROK_SPACE },
|
||||
{ DOSKEY_RALT, RETROK_RALT },
|
||||
{ DOSKEY_RSUPER, RETROK_RSUPER },
|
||||
{ DOSKEY_MENU, RETROK_MENU },
|
||||
{ DOSKEY_RCTRL, RETROK_RCTRL },
|
||||
|
||||
{ DOSKEY_UP, RETROK_UP },
|
||||
{ DOSKEY_DOWN, RETROK_DOWN },
|
||||
{ DOSKEY_LEFT, RETROK_LEFT },
|
||||
{ DOSKEY_RIGHT, RETROK_RIGHT },
|
||||
|
||||
{ DOSKEY_HOME, RETROK_HOME },
|
||||
{ DOSKEY_END, RETROK_END },
|
||||
{ DOSKEY_PGUP, RETROK_PAGEUP },
|
||||
{ DOSKEY_PGDN, RETROK_PAGEDOWN },
|
||||
|
||||
{ 0, RETROK_UNKNOWN }
|
||||
};
|
||||
#endif
|
||||
|
||||
static enum retro_key rarch_keysym_lut[RETROK_LAST];
|
||||
|
||||
/**
|
||||
|
@ -57,6 +57,8 @@ extern const struct rarch_key_map rarch_key_map_linux[];
|
||||
extern const struct rarch_key_map rarch_key_map_apple_hid[];
|
||||
extern const struct rarch_key_map rarch_key_map_android[];
|
||||
extern const struct rarch_key_map rarch_key_map_qnx[];
|
||||
extern const struct rarch_key_map rarch_key_map_dos[];
|
||||
extern const struct rarch_key_map rarch_key_map_wiiu[];
|
||||
|
||||
/**
|
||||
* input_keymaps_init_keyboard_lut:
|
||||
|
@ -30,6 +30,7 @@
|
||||
#endif
|
||||
|
||||
#include "../verbosity.h"
|
||||
#include "../gfx/video_driver.h"
|
||||
|
||||
#include "input_overlay.h"
|
||||
#include "input_keyboard.h"
|
||||
|
@ -1,42 +1,42 @@
|
||||
MSG_HASH(
|
||||
MSG_COMPILER,
|
||||
"Compiler"
|
||||
"编译器"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_UNKNOWN_COMPILER,
|
||||
"Unknown compiler"
|
||||
"未知的编译器"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_DISCONNECTED_FROM_PORT,
|
||||
"Device disconnected from port"
|
||||
"设备已从端口上断开"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED,
|
||||
"Unknown netplay command received"
|
||||
"接收到未知的联机游戏指令"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_FILE_ALREADY_EXISTS_SAVING_TO_BACKUP_BUFFER,
|
||||
"文件已存在. Saving to backup buffer"
|
||||
"文件已存在。保存到备份缓冲区"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_GOT_CONNECTION_FROM,
|
||||
"Got connection from: \"%s\""
|
||||
"连接来自: \"%s\""
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_GOT_CONNECTION_FROM_NAME,
|
||||
"Got connection from: \"%s (%s)\""
|
||||
"连接来自: \"%s (%s)\""
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_PUBLIC_ADDRESS,
|
||||
"Public address"
|
||||
"公开地址"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
|
||||
"No arguments supplied and no menu builtin, displaying help..."
|
||||
"未提供参数也没有内建菜单,显示帮助..."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_USERS_HAS_FLIPPED,
|
||||
"Netplay users has flipped"
|
||||
"联机游戏用户已被踢出"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_SETTING_DISK_IN_TRAY,
|
||||
@ -44,19 +44,19 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_WAITING_FOR_CLIENT,
|
||||
"Waiting for client ..."
|
||||
"等待客户端 ..."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_YOU_HAVE_LEFT_THE_GAME,
|
||||
"You have left the game"
|
||||
"你已离开游戏"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_YOU_HAVE_JOINED_AS_PLAYER_N,
|
||||
"You have joined as player %d"
|
||||
"你已作为玩家 %d 加入"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_IMPLEMENTATIONS_DIFFER,
|
||||
"Implementations differ. Make sure you're using the exact same versions of RetroArch and the core."
|
||||
"实现有差异。确保正在使用的RetroArch和核心是同版本的。"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_ENDIAN_DEPENDENT,
|
||||
@ -68,43 +68,43 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_ENTER_PASSWORD,
|
||||
"Enter netplay server password:"
|
||||
"输入联机游戏服务器的密码:"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_INCORRECT_PASSWORD,
|
||||
"Incorrect password"
|
||||
"密码错误"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_SERVER_NAMED_HANGUP,
|
||||
"\"%s\" has disconnected"
|
||||
"\"%s\" 已断开连接"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_SERVER_HANGUP,
|
||||
"A netplay client has disconnected"
|
||||
"一个联机游戏客户端已断开"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_CLIENT_HANGUP,
|
||||
"Netplay disconnected"
|
||||
"联机游戏已断开"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_CANNOT_PLAY_UNPRIVILEGED,
|
||||
"You do not have permission to play"
|
||||
"你没有游戏权限"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_CANNOT_PLAY_NO_SLOTS,
|
||||
"There are no free player slots"
|
||||
"已无空闲插槽" /*FIXME:"There are no free player slots"*/
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_CANNOT_PLAY,
|
||||
"Cannot switch to play mode"
|
||||
"无法切换到游戏模式"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_PEER_PAUSED,
|
||||
"Netplay peer \"%s\" paused"
|
||||
"联机游戏对方 \"%s\" 暂停"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_CHANGED_NICK,
|
||||
"Your nickname changed to \"%s\""
|
||||
"你的昵称已修改为 \"%s\""
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT,
|
||||
@ -133,27 +133,27 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_AUTOLOADING_SAVESTATE_FROM,
|
||||
"Auto-loading savestate from"
|
||||
"自动加载存档从"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CAPABILITIES,
|
||||
"Capabilities"
|
||||
"容量"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTING_TO_NETPLAY_HOST,
|
||||
"Connecting to netplay host"
|
||||
"连接到联机游戏主机"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTING_TO_PORT,
|
||||
"Connecting to port"
|
||||
"连接到端口"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTION_SLOT,
|
||||
"Connection slot"
|
||||
"连接到插槽"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_SORRY_UNIMPLEMENTED_CORES_DONT_DEMAND_CONTENT_NETPLAY,
|
||||
"Sorry, unimplemented: cores that don't demand content cannot participate in netplay."
|
||||
"对不起,未实现:核心未请求内容,无法加入联机游戏。"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_FAILED_TO_SET_DISK,
|
||||
@ -164,7 +164,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_SETTINGS,
|
||||
"Accounts Cheevos"
|
||||
"Cheevos账户" /*FIXME:"Accounts Cheevos"*/
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_USERNAME,
|
||||
@ -189,7 +189,7 @@ MSG_HASH(
|
||||
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST_HARDCORE,
|
||||
"Achievement List (Hardcore)"
|
||||
"成就列表(硬核)" /*FIXME:"Achievement List (Hardcore)"*/
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST,
|
||||
@ -205,7 +205,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_TAB,
|
||||
"Netplay Rooms"
|
||||
"联机游戏房间"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ARCHIVE_MODE,
|
||||
@ -221,7 +221,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUDIO_BLOCK_FRAMES,
|
||||
"Block Frames"
|
||||
"块帧" /*FIXME:"Block Frames"*/
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUDIO_DEVICE,
|
||||
@ -245,7 +245,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_TURBO_DEADZONE_LIST,
|
||||
"Turbo/Deadzone"
|
||||
"涡轮/盲区"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUDIO_LATENCY,
|
||||
@ -417,7 +417,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEAT_NUM_PASSES,
|
||||
"Cheat Passes"
|
||||
"金手指通过" /*FIXME: "Cheat Passes"*/
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_DESCRIPTION,
|
||||
@ -598,9 +598,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DRIVER_SETTINGS,
|
||||
"驱动")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DUMMY_ON_CORE_SHUTDOWN,
|
||||
/* FIXME? Translate 'Load Dummy on Core Shutdown' */
|
||||
"Load Dummy on Core Shutdown")
|
||||
"核心关闭时加载虚拟程序")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CHECK_FOR_MISSING_FIRMWARE,
|
||||
"Check for Missing Firmware Before Loading")
|
||||
"加载前检查丢失的固件") /*FIXME: "Check for Missing Firmware Before Loading"*/
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPER,
|
||||
"动态壁纸")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPERS_DIRECTORY,
|
||||
@ -691,7 +691,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AUTODETECT_ENABLE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_AXIS_THRESHOLD,
|
||||
"输入轴阈值")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_MENU_INPUT_SWAP_OK_CANCEL,
|
||||
"Menu Swap OK & Cancel Buttons")
|
||||
"菜单切换 确定/取消 按钮") /*FIXME:"Menu Swap OK & Cancel Buttons"*/
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_ALL,
|
||||
"绑定全部")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL,
|
||||
@ -747,7 +747,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_X,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_JOYPAD_Y,
|
||||
"Y键(左侧)")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEY,
|
||||
"(Key: %s)")
|
||||
"(键: %s)") /*FIXME:"(Key: %s)"*/
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_KEYBOARD_GAMEPAD_MAPPING_TYPE,
|
||||
"键盘控制器映射类型")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_MAX_USERS,
|
||||
@ -789,9 +789,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MOVIE_RECORD_TOGGLE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_MUTE,
|
||||
"静音开关")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_FLIP,
|
||||
"Netplay flip users")
|
||||
"联机游戏踢出用户")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_NETPLAY_GAME_WATCH,
|
||||
"Netplay toggle play/spectate mode")
|
||||
"联机游戏切换 游戏/围观 模式")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OSK,
|
||||
"切换屏幕键盘")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_META_OVERLAY_NEXT,
|
||||
@ -999,19 +999,19 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_MODE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NICKNAME,
|
||||
"用户名")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD,
|
||||
"Server Password")
|
||||
"服务器密码")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS,
|
||||
"在线游戏设置")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_STATELESS_MODE,
|
||||
"Netplay Stateless Mode")
|
||||
"联机无状态模式")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATE_PASSWORD,
|
||||
"Server Spectate-Only Password")
|
||||
"服务器围观的密码")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SPECTATOR_MODE_ENABLE,
|
||||
"启用在线游戏旁观者")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_TCP_UDP_PORT,
|
||||
"在线游戏TCP/UDP端口")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NAT_TRAVERSAL,
|
||||
"Netplay NAT Traversal")
|
||||
"联机NAT遍历")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_ENABLE,
|
||||
"网络命令")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_CMD_PORT,
|
||||
@ -1135,11 +1135,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DESCRIPTION,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DEVELOPER,
|
||||
"开发者")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_ISSUE,
|
||||
"Edge Magazine Issue")
|
||||
"Edge杂志发行")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_RATING,
|
||||
"Edge Magazine Rating")
|
||||
"Edge杂志评分")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_EDGE_MAGAZINE_REVIEW,
|
||||
"Edge Magazine Review")
|
||||
"Edge杂志评论")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ELSPA_RATING,
|
||||
"ELSPA 分级")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ENHANCEMENT_HW,
|
||||
@ -1147,7 +1147,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ENHANCEMENT_HW,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_ESRB_RATING,
|
||||
"ESRB 分级")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FAMITSU_MAGAZINE_RATING,
|
||||
"Famitsu Magazine Rating")
|
||||
"次世代(Famitsu)杂志评分")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_FRANCHISE,
|
||||
"经销商")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_GENRE,
|
||||
@ -1175,7 +1175,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_SHA1,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_START_CONTENT,
|
||||
"启动游戏内容")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RDB_ENTRY_TGDB_RATING,
|
||||
"TGDB Rating")
|
||||
"TGDB 评分")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_REBOOT,
|
||||
"重启")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORDING_CONFIG_DIRECTORY,
|
||||
@ -1213,9 +1213,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RESUME,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RESUME_CONTENT,
|
||||
"继续")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROKEYBOARD,
|
||||
"RetroKeyboard")
|
||||
"Retro键盘")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD,
|
||||
"RetroPad")
|
||||
"Retro触摸板")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RETROPAD_WITH_ANALOG,
|
||||
"RetroPad w/ Analog")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RETRO_ACHIEVEMENTS_SETTINGS,
|
||||
@ -1631,7 +1631,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_TAB,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_THREADED,
|
||||
"多线程渲染")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VFILTER,
|
||||
"Deflicker")
|
||||
"降低闪烁")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_HEIGHT,
|
||||
"自定义视口高度")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_WIDTH,
|
||||
@ -1641,7 +1641,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_X,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VIEWPORT_CUSTOM_Y,
|
||||
"自定义视口Y")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VI_WIDTH,
|
||||
"Set VI Screen Width")
|
||||
"设置 VI 屏幕宽度")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_VSYNC,
|
||||
"垂直同步")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_VIDEO_WINDOWED_FULLSCREEN,
|
||||
@ -1705,7 +1705,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHADOWS_ENABLE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHOW_HISTORY,
|
||||
"显示历史页")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHOW_ADD,
|
||||
"Display Import content Tab")
|
||||
"显示导入内容页")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHOW_IMAGES,
|
||||
"显示图像页")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_XMB_SHOW_MUSIC,
|
||||
@ -1721,11 +1721,11 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_YES,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VIDEO_SHADER_PRESET_TWO,
|
||||
"Shader预设")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_ENABLE,
|
||||
"Enable or disable achievements. For more information, visit http://retroachievements.org")
|
||||
"打开或关闭成就。更多内容请访问 http://retroachievements.org")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL,
|
||||
"Enable or disable unofficial achievements and/or beta features for testing purposes.")
|
||||
"为测试目的而打开或关闭非官方成就和/或测试版特性。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE,
|
||||
"Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.")
|
||||
"为所有游戏打开或关闭存档、金手指、回退、快进、暂停和慢动作。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS,
|
||||
"修改驱动设置。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS,
|
||||
@ -1733,7 +1733,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_SETTINGS,
|
||||
"修改核心设置。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_SETTINGS,
|
||||
"Change settings for the recording.")
|
||||
"修改录制的设置。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONSCREEN_DISPLAY_SETTINGS,
|
||||
"修改显示覆盖、键盘覆盖和屏幕通知的设置。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_FRAME_THROTTLE_SETTINGS,
|
||||
@ -1804,9 +1804,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION,
|
||||
"在帧与帧之间插入黑色的中间帧,通常用于消除在\n"
|
||||
"120Hz刷新率的显示器上运行60Hz的游戏内容带来\n"
|
||||
"的鬼影。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY,
|
||||
"以增加画面卡顿的风险换取低延时,在垂直同步后增加\n"
|
||||
"时延(毫秒)。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY,
|
||||
"以增加画面卡顿的风险换取低延时,在垂直同步后增加\n"
|
||||
"时延(毫秒)。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC_FRAMES,
|
||||
"设置当开启“强制GPU同步”时CPU可以预先GPU多少帧。")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES,
|
||||
@ -1832,35 +1832,35 @@ MSG_HASH(MSG_AUDIO_MUTED,
|
||||
MSG_HASH(MSG_AUDIO_UNMUTED,
|
||||
"取消静音。")
|
||||
MSG_HASH(MSG_AUTOCONFIG_FILE_ERROR_SAVING,
|
||||
"Error saving autoconf file.")
|
||||
"保存 autoconf 文件错误。")
|
||||
MSG_HASH(MSG_AUTOCONFIG_FILE_SAVED_SUCCESSFULLY,
|
||||
"Autoconfig file saved successfully.")
|
||||
"自动配置文件保存成功。")
|
||||
MSG_HASH(MSG_AUTOSAVE_FAILED,
|
||||
"Could not initialize autosave.")
|
||||
"无法初始化自动保存。")
|
||||
MSG_HASH(MSG_AUTO_SAVE_STATE_TO,
|
||||
"自动保存状态至")
|
||||
MSG_HASH(MSG_BLOCKING_SRAM_OVERWRITE,
|
||||
"Blocking SRAM Overwrite")
|
||||
"阻止 SRAM 覆盖")
|
||||
MSG_HASH(MSG_BRINGING_UP_COMMAND_INTERFACE_ON_PORT,
|
||||
"Bringing up command interface on port")
|
||||
MSG_HASH(MSG_BYTES,
|
||||
"bytes")
|
||||
"字节")
|
||||
MSG_HASH(MSG_CANNOT_INFER_NEW_CONFIG_PATH,
|
||||
"Cannot infer new config path. Use current time.")
|
||||
"无法推断新的配置路径,使用当前时间。")
|
||||
MSG_HASH(MSG_CHEEVOS_HARDCORE_MODE_ENABLE,
|
||||
"硬核模式开启:及时存档和回放被禁用.")
|
||||
MSG_HASH(MSG_COMPARING_WITH_KNOWN_MAGIC_NUMBERS,
|
||||
"Comparing with known magic numbers...")
|
||||
"与已知的magic numbers比较...")
|
||||
MSG_HASH(MSG_COMPILED_AGAINST_API,
|
||||
"Compiled against API")
|
||||
MSG_HASH(MSG_CONFIG_DIRECTORY_NOT_SET,
|
||||
"Config directory not set. Cannot save new config.")
|
||||
"未设置配置目录,无法保存新的配置。")
|
||||
MSG_HASH(MSG_CONNECTED_TO,
|
||||
"连接至")
|
||||
MSG_HASH(MSG_CONTENT_CRC32S_DIFFER,
|
||||
"Content CRC32s differ. Cannot use different games.")
|
||||
"内容的CRC32s不同。无法使用不同的游戏。")
|
||||
MSG_HASH(MSG_CONTENT_LOADING_SKIPPED_IMPLEMENTATION_WILL_DO_IT,
|
||||
"Content loading skipped. Implementation will load it on its own.")
|
||||
"跳过内容加载。实现将自行加载。")
|
||||
MSG_HASH(MSG_CORE_DOES_NOT_SUPPORT_SAVESTATES,
|
||||
"核心不支持保存状态。")
|
||||
MSG_HASH(MSG_CORE_OPTIONS_FILE_CREATED_SUCCESSFULLY,
|
||||
@ -2066,25 +2066,25 @@ MSG_HASH(MSG_LOADING_STATE,
|
||||
MSG_HASH(MSG_MEMORY,
|
||||
"内存")
|
||||
MSG_HASH(MSG_MOVIE_FILE_IS_NOT_A_VALID_BSV1_FILE,
|
||||
"Movie file is not a valid BSV1 file.")
|
||||
"视频不是有效的BSV1文件。")
|
||||
MSG_HASH(MSG_MOVIE_FORMAT_DIFFERENT_SERIALIZER_VERSION,
|
||||
"Movie format seems to have a different serializer version. Will most likely fail.")
|
||||
"视频格式看起来使用了不同的序列化版本。很有可能失败。")
|
||||
MSG_HASH(MSG_MOVIE_PLAYBACK_ENDED,
|
||||
"视频回放结束.")
|
||||
MSG_HASH(MSG_MOVIE_RECORD_STOPPED,
|
||||
"Stopping movie record.")
|
||||
"停止视频录制。")
|
||||
MSG_HASH(MSG_NETPLAY_FAILED,
|
||||
"Failed to initialize netplay.")
|
||||
"初始化联机游戏失败。")
|
||||
MSG_HASH(MSG_NO_CONTENT_STARTING_DUMMY_CORE,
|
||||
"No content, starting dummy core.")
|
||||
"没有内容,启动虚拟核心。")
|
||||
MSG_HASH(MSG_NO_SAVE_STATE_HAS_BEEN_OVERWRITTEN_YET,
|
||||
"No save state has been overwritten yet.")
|
||||
"未覆盖任何存档。")
|
||||
MSG_HASH(MSG_NO_STATE_HAS_BEEN_LOADED_YET,
|
||||
"No state has been loaded yet.")
|
||||
"没有加载任何存档。")
|
||||
MSG_HASH(MSG_OVERRIDES_ERROR_SAVING,
|
||||
"Error saving overrides.")
|
||||
"保存覆盖错误。")
|
||||
MSG_HASH(MSG_OVERRIDES_SAVED_SUCCESSFULLY,
|
||||
"Overrides saved successfully.")
|
||||
"覆盖保存成功。")
|
||||
MSG_HASH(MSG_PAUSED,
|
||||
"暂停。")
|
||||
MSG_HASH(MSG_PROGRAM,
|
||||
@ -2094,9 +2094,9 @@ MSG_HASH(MSG_READING_FIRST_DATA_TRACK,
|
||||
MSG_HASH(MSG_RECEIVED,
|
||||
"接收完毕")
|
||||
MSG_HASH(MSG_RECORDING_TERMINATED_DUE_TO_RESIZE,
|
||||
"Recording terminated due to resize.")
|
||||
"录制因改变大小而停止。")
|
||||
MSG_HASH(MSG_RECORDING_TO,
|
||||
"Recording to")
|
||||
"录制到")
|
||||
MSG_HASH(MSG_REDIRECTING_CHEATFILE_TO,
|
||||
"重定向金手指文件至")
|
||||
MSG_HASH(MSG_REDIRECTING_SAVEFILE_TO,
|
||||
@ -2112,9 +2112,9 @@ MSG_HASH(MSG_REMOVING_TEMPORARY_CONTENT_FILE,
|
||||
MSG_HASH(MSG_RESET,
|
||||
"重置")
|
||||
MSG_HASH(MSG_RESTARTING_RECORDING_DUE_TO_DRIVER_REINIT,
|
||||
"Restarting recording due to driver reinit.")
|
||||
"重启录制由于驱动器重新初始化。")
|
||||
MSG_HASH(MSG_RESTORED_OLD_SAVE_STATE,
|
||||
"Restored old save state.")
|
||||
"重载旧的存档。")
|
||||
MSG_HASH(MSG_RESTORING_DEFAULT_SHADER_PRESET_TO,
|
||||
"Shaders: restoring default shader preset to")
|
||||
MSG_HASH(MSG_REVERTING_SAVEFILE_DIRECTORY_TO,
|
||||
@ -2140,15 +2140,15 @@ MSG_HASH(MSG_SAVED_STATE_TO_SLOT_AUTO,
|
||||
MSG_HASH(MSG_SAVED_SUCCESSFULLY_TO,
|
||||
"成功保存至")
|
||||
MSG_HASH(MSG_SAVING_RAM_TYPE,
|
||||
"Saving RAM type")
|
||||
"保存 RAM 类型")
|
||||
MSG_HASH(MSG_SAVING_STATE,
|
||||
"Saving state")
|
||||
"存档中")
|
||||
MSG_HASH(MSG_SCANNING,
|
||||
"扫描中")
|
||||
MSG_HASH(MSG_SCANNING_OF_DIRECTORY_FINISHED,
|
||||
"已完成对文件夹的扫描")
|
||||
MSG_HASH(MSG_SENDING_COMMAND,
|
||||
"Sending command")
|
||||
"发送指令")
|
||||
MSG_HASH(MSG_SEVERAL_PATCHES_ARE_EXPLICITLY_DEFINED,
|
||||
"Several patches are explicitly defined, ignoring all...")
|
||||
MSG_HASH(MSG_SHADER,
|
||||
@ -2156,7 +2156,7 @@ MSG_HASH(MSG_SHADER,
|
||||
MSG_HASH(MSG_SHADER_PRESET_SAVED_SUCCESSFULLY,
|
||||
"Shader preset saved successfully.")
|
||||
MSG_HASH(MSG_SKIPPING_SRAM_LOAD,
|
||||
"Skipping SRAM load.")
|
||||
"跳过 SRAM 加载。")
|
||||
MSG_HASH(MSG_SLOW_MOTION,
|
||||
"慢动作。")
|
||||
MSG_HASH(MSG_SLOW_MOTION_REWIND,
|
||||
@ -2172,9 +2172,9 @@ MSG_HASH(MSG_STATE_SIZE,
|
||||
MSG_HASH(MSG_STATE_SLOT,
|
||||
"状态存档槽")
|
||||
MSG_HASH(MSG_TAKING_SCREENSHOT,
|
||||
"Taking screenshot.")
|
||||
"截屏。")
|
||||
MSG_HASH(MSG_TO,
|
||||
"to")
|
||||
"到")
|
||||
MSG_HASH(MSG_UNDID_LOAD_STATE,
|
||||
"已撤销加载状态。")
|
||||
MSG_HASH(MSG_UNDOING_SAVE_STATE,
|
||||
@ -2184,15 +2184,15 @@ MSG_HASH(MSG_UNKNOWN,
|
||||
MSG_HASH(MSG_UNPAUSED,
|
||||
"取消暂停。")
|
||||
MSG_HASH(MSG_UNRECOGNIZED_COMMAND,
|
||||
"Unrecognized command")
|
||||
"无法识别的指令")
|
||||
MSG_HASH(MSG_USING_CORE_NAME_FOR_NEW_CONFIG,
|
||||
"Using core name for new config.")
|
||||
MSG_HASH(MSG_USING_LIBRETRO_DUMMY_CORE_RECORDING_SKIPPED,
|
||||
"Using libretro dummy core. Skipping recording.")
|
||||
"使用libretro虚拟核心。跳过录制。")
|
||||
MSG_HASH(MSG_VALUE_CONNECT_DEVICE_FROM_A_VALID_PORT,
|
||||
"Connect device from a valid port.")
|
||||
MSG_HASH(MSG_VALUE_DISCONNECTING_DEVICE_FROM_PORT,
|
||||
"Disconnecting device from port")
|
||||
"从端口断开设备")
|
||||
MSG_HASH(MSG_VALUE_REBOOTING,
|
||||
"正在重启……")
|
||||
MSG_HASH(MSG_VALUE_SHUTTING_DOWN,
|
||||
@ -2206,7 +2206,7 @@ MSG_HASH(MSG_VIRTUAL_DISK_TRAY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_LATENCY,
|
||||
"Desired audio latency in milliseconds. Might not be honored if the audio driver can't provide given latency.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MUTE,
|
||||
"Mute/unmute audio.")
|
||||
"禁音/取消禁音。")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA,
|
||||
"Helps smooth out imperfections in timing when synchronizing audio and video at the same time. Be aware that if disabled, proper synchronization is nearly impossible to obtain."
|
||||
@ -2237,7 +2237,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_SYNC,
|
||||
"Synchronize audio. Recommended."
|
||||
"同步音频。推荐。"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_INPUT_AXIS_THRESHOLD,
|
||||
@ -2295,6 +2295,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"未配置"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"数据库 Cursor List"
|
||||
@ -2363,38 +2367,38 @@ MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CONTENT_HISTORY_SIZE,
|
||||
"游戏、图片、音乐和视频历史记录的数量限制。")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_INPUT_UNIFIED_MENU_CONTROLS,
|
||||
"Unified Menu Controls")
|
||||
"统一菜单控制")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_UNIFIED_MENU_CONTROLS,
|
||||
"Use the same controls for both the menu and the game. Applies to the keyboard.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_ENABLE,
|
||||
"Show onscreen messages.")
|
||||
"显示屏幕消息。")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETWORK_USER_REMOTE_ENABLE,
|
||||
"User %d Remote Enable")
|
||||
"用户 %d 远程允许")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_BATTERY_LEVEL_ENABLE,
|
||||
"Display battery level")
|
||||
"显示电池电量")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FILE,
|
||||
"Select File")
|
||||
"选择文件")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SELECT_FROM_COLLECTION,
|
||||
"Select From Collection")
|
||||
"从收藏中选择")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FILTER,
|
||||
"Filter")
|
||||
"过滤器")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SCALE,
|
||||
"Scale")
|
||||
"刻度")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_START_WHEN_LOADED,
|
||||
"Netplay will start when content is loaded.")
|
||||
"联机游戏将在内容加载后开始。")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_LOAD_CONTENT_MANUALLY,
|
||||
"Couldn't find a suitable core or content file, load manually.")
|
||||
"无法找到合适的核心或内容文件,手动加载。")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BROWSE_URL_LIST,
|
||||
"Browse URL"
|
||||
"浏览URL"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BROWSE_URL,
|
||||
"URL Path"
|
||||
"URL路径"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BROWSE_START,
|
||||
"Start"
|
||||
"开始"
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_SHADER_PIPELINE_BOKEH,
|
||||
"Bokeh")
|
||||
|
1965
intl/msg_hash_de.c
1965
intl/msg_hash_de.c
File diff suppressed because it is too large
Load Diff
2789
intl/msg_hash_de.h
2789
intl/msg_hash_de.h
File diff suppressed because it is too large
Load Diff
@ -1675,7 +1675,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
"Join or host a netplay session.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
|
@ -1,14 +1,14 @@
|
||||
MSG_HASH(
|
||||
MSG_COMPILER,
|
||||
"Compiler"
|
||||
"Compilateur"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_UNKNOWN_COMPILER,
|
||||
"Unknown compiler"
|
||||
"Compilateur inconnu"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_DISCONNECTED_FROM_PORT,
|
||||
"Device disconnected from port"
|
||||
"Périphérique déconnecté (du port si 'port X')"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_UNKNOWN_NETPLAY_COMMAND_RECEIVED,
|
||||
@ -40,7 +40,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_WAITING_FOR_CLIENT,
|
||||
"Waiting for client ..."
|
||||
"En attente du client..."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_SHARED_CONTEXT,
|
||||
@ -48,43 +48,43 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_MENU_SETTINGS,
|
||||
"Adjusts settings related to the appearance of the menu screen."
|
||||
"Ajuste les réglages liés à l'apparence de l'écran de menu."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC,
|
||||
"Hard-synchronize the CPU and GPU. Reduces latency at the cost of performance."
|
||||
"Synchronisation 'hard' du CPU et du GPU. Réduit la latence au prix de la performance."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_THREADED,
|
||||
"Improves performance at the cost of latency and more video stuttering. Use only if you cannot obtain full speed otherwise."
|
||||
"Améliore les performances au prix d'une certaine latence et d'à-coups visuels accrus. À n'utiliser que si vous ne pouvez pas faire autrement."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_AUDIO_VOLUME,
|
||||
"Audio volume"
|
||||
"Volume sonore"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_AUTODETECT,
|
||||
"Autodetect"
|
||||
"Détection automatique"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_AUTOLOADING_SAVESTATE_FROM,
|
||||
"Auto-loading savestate from"
|
||||
"Chargement auto d'une savestate depuis"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CAPABILITIES,
|
||||
"Capabilities"
|
||||
"Capacités"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTING_TO_NETPLAY_HOST,
|
||||
"Connecting to netplay host"
|
||||
"Connexion à l'hôte"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTING_TO_PORT,
|
||||
"Connecting to port"
|
||||
"Connexion au port"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_CONNECTION_SLOT,
|
||||
"Connection slot"
|
||||
"Slot de connexion"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_SORRY_UNIMPLEMENTED_CORES_DONT_DEMAND_CONTENT_NETPLAY,
|
||||
@ -92,7 +92,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_PASSWORD,
|
||||
"Password"
|
||||
"Mot de passe"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_SETTINGS,
|
||||
@ -100,7 +100,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_CHEEVOS_USERNAME,
|
||||
"Username"
|
||||
"Nom d'utilisateur"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_LIST,
|
||||
@ -112,16 +112,16 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACCOUNTS_RETRO_ACHIEVEMENTS,
|
||||
"Retro Achievements"
|
||||
"Succès Retro"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST,
|
||||
"Achievement List"
|
||||
"Liste des Succès"
|
||||
)
|
||||
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST_HARDCORE,
|
||||
"Achievement List (Hardcore)"
|
||||
"Liste des Succès (hardcore)"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ADD_CONTENT_LIST,
|
||||
@ -209,11 +209,11 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUDIO_VOLUME,
|
||||
"Volume sonnore (dB)"
|
||||
"Volume sonore (dB)"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUTOSAVE_INTERVAL,
|
||||
"Intervale de sauvegarde SaveRAM"
|
||||
"Intervalle de sauvegarde SaveRAM"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE,
|
||||
@ -225,63 +225,63 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_AUTO_SHADERS_ENABLE,
|
||||
"Load Shader Presets Automatically"
|
||||
"Charger les pré-réglages de shaders automatiquement"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_BACK,
|
||||
"Back"
|
||||
"Retour"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_CONFIRM,
|
||||
"Confirm"
|
||||
"Confirmer"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_INFO,
|
||||
"Info"
|
||||
"Information"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_QUIT,
|
||||
"Quit"
|
||||
"Quitter"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_SCROLL_DOWN,
|
||||
"Scroll Down"
|
||||
"Défilement vers le bas"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_SCROLL_UP,
|
||||
"Scroll Up"
|
||||
"Défilement vers le haut"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_START,
|
||||
"Start"
|
||||
"Démarrer"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_KEYBOARD,
|
||||
"Toggle Keyboard"
|
||||
"Basculer sur le Clavier"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_CONTROLS_TOGGLE_MENU,
|
||||
"Toggle Menu"
|
||||
"Basculer sur le Menu"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS,
|
||||
"Basic menu controls"
|
||||
"Contrôles du menu basiques"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_CONFIRM,
|
||||
"Confirm/OK"
|
||||
"Confirmer / OK"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_INFO,
|
||||
"Info"
|
||||
"Information"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_QUIT,
|
||||
"Quit"
|
||||
"Quitter"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_SCROLL_UP,
|
||||
"Scroll Up"
|
||||
"Défilement vers le haut"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_START,
|
||||
@ -289,11 +289,11 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_KEYBOARD,
|
||||
"Toggle Keyboard"
|
||||
"Basculer sur le Klavier"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BASIC_MENU_ENUM_CONTROLS_TOGGLE_MENU,
|
||||
"Toggle Menu"
|
||||
"Basculer sur le Menu"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BLOCK_SRAM_OVERWRITE,
|
||||
@ -301,7 +301,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BLUETOOTH_ENABLE,
|
||||
"Bluetooth Enable"
|
||||
"Activer le Bluetooth"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_BUILDBOT_ASSETS_URL,
|
||||
@ -333,15 +333,15 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEAT_FILE,
|
||||
"Cheat File"
|
||||
"Fichier de Triche"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEAT_FILE_LOAD,
|
||||
"Load Cheat File"
|
||||
"Charger un fichier de Triche"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEAT_FILE_SAVE_AS,
|
||||
"Save Cheat File As"
|
||||
"Enregistrer le fichier de Triche sous"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEAT_NUM_PASSES,
|
||||
@ -353,19 +353,19 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_HARDCORE_MODE_ENABLE,
|
||||
"Achievements Hardcore Mode"
|
||||
"Mode Hardcore des Succès"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ACHIEVEMENTS,
|
||||
"Locked Achievements:"
|
||||
"Succès verrouillés:"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_LOCKED_ENTRY,
|
||||
"Locked"
|
||||
"Verrouillé"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_SETTINGS,
|
||||
"Retro Achievements"
|
||||
"Succès Retro"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_TEST_UNOFFICIAL,
|
||||
@ -373,11 +373,11 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ACHIEVEMENTS,
|
||||
"Unlocked Achievements:"
|
||||
"Succès déverrouillés:"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CHEEVOS_UNLOCKED_ENTRY,
|
||||
"Unlocked"
|
||||
"Déverrouillé"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT,
|
||||
@ -385,11 +385,11 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONFIG,
|
||||
"Config"
|
||||
"Configuration"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONFIGURATIONS,
|
||||
"Load Configuration"
|
||||
"Charger la configuration"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONFIGURATION_SETTINGS,
|
||||
@ -405,7 +405,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_DATABASE_DIRECTORY,
|
||||
"Dossier des bases de données de contenus")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONTENT_DIR,
|
||||
"Content"
|
||||
"Contenu"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONTENT_HISTORY_SIZE,
|
||||
@ -413,13 +413,13 @@ MSG_HASH(
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CONTENT_SETTINGS,
|
||||
"Menu rapide")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIR,
|
||||
"Core Assets")
|
||||
"Assets de Coeur")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ASSETS_DIRECTORY,
|
||||
"Dossier des téléchargements")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_CHEAT_OPTIONS,
|
||||
"Triche")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_COUNTERS,
|
||||
"Core Counters")
|
||||
"Compteurs de Cores")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_ENABLE,
|
||||
"Afficher le coeur actuel")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CORE_INFORMATION,
|
||||
@ -475,9 +475,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_CUSTOM_RATIO,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_MANAGER,
|
||||
"Gestion de la base de données")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DATABASE_SELECTION,
|
||||
"Database Selection")
|
||||
"Sélection de la base de données")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DELETE_ENTRY,
|
||||
"Remove")
|
||||
"Supprimer")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FAVORITES,
|
||||
"Via les fichiers") /* TODO/FIXME - update */
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DIRECTORY_CONTENT,
|
||||
@ -519,7 +519,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPER,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_DYNAMIC_WALLPAPERS_DIRECTORY,
|
||||
"Dossier des fonds d'écran dynamiques")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_CHEEVOS_ENABLE,
|
||||
"Enable Achievements")
|
||||
"Activer les Succès")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_ENTRY_HOVER_COLOR,
|
||||
"Couleur de l'entrée active")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_ENTRY_NORMAL_COLOR,
|
||||
@ -529,7 +529,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FALSE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FASTFORWARD_RATIO,
|
||||
"Vitesse de l'avance rapide")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FPS_SHOW,
|
||||
"Afficher le FPS")
|
||||
"Afficher les i/s")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_ENABLE,
|
||||
"Limiter la vitesse d'exécution")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS,
|
||||
@ -537,17 +537,17 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_FRAME_THROTTLE_SETTINGS,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_FRONTEND_COUNTERS,
|
||||
"Compteurs du Frontend")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS,
|
||||
"Options du coeur par-jeu")
|
||||
"Options du coeur par jeu")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE,
|
||||
"Create game-options file")
|
||||
"Créer un fichier d'options par jeu")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE,
|
||||
"Game-options file")
|
||||
"Fichier d'option par jeu")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP,
|
||||
"Aide")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING,
|
||||
"Audio/Video Troubleshooting")
|
||||
"Dépannage audio / vidéo")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD,
|
||||
"Changing Virtual Gamepad Overlay")
|
||||
"Modifier l'overlay de la manette virtuelle")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CONTROLS,
|
||||
"Basic Menu Controls")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_LIST,
|
||||
@ -1083,9 +1083,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_RECORD_USE_OUTPUT_DIRECTORY,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_LOAD,
|
||||
"Charger un fichier de remap")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_CORE,
|
||||
"Charger un fichier remaps de coeur")
|
||||
"Sauvegarder un fichier remaps de coeur")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_REMAP_FILE_SAVE_GAME,
|
||||
"Charger un fichier remap de contenu")
|
||||
"Sauvegarder un fichier remap de contenu")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_REQUIRED,
|
||||
"Requis")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_RESTART_CONTENT,
|
||||
@ -1643,7 +1643,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
"Join or host a netplay session.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
@ -2148,6 +2148,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"not configured"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"Database Cursor List"
|
||||
|
@ -1659,7 +1659,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS,
|
||||
"Search for and connect to netplay hosts on the local network.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
@ -2170,6 +2170,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"not configured"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"Database Cursor List"
|
||||
|
@ -2291,6 +2291,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"設定されていない"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"データベースのカーソル表"
|
||||
|
@ -611,6 +611,8 @@ MSG_HASH(MENU_ENUM_LABEL_NETPLAY_PASSWORD,
|
||||
"netplay_password")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SETTINGS,
|
||||
"menu_netplay_settings")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
"netplay_public_announce")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATE_PASSWORD,
|
||||
"netplay_spectate_password")
|
||||
MSG_HASH(MENU_ENUM_LABEL_NETPLAY_SPECTATOR_MODE_ENABLE,
|
||||
|
@ -1675,7 +1675,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
"Join or host a netplay session.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
@ -2182,6 +2182,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"not configured"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"Database Cursor List"
|
||||
|
@ -1,4 +1,4 @@
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
#if defined(_MSC_VER) && !defined(_XBOX)
|
||||
/* https://support.microsoft.com/en-us/kb/980263 */
|
||||
#pragma execution_character_set("utf-8")
|
||||
#endif
|
||||
@ -1676,7 +1676,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
"Join or host a netplay session.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
@ -2181,6 +2181,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"not configured"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"Database Cursor List"
|
||||
|
@ -1535,6 +1535,13 @@ int menu_hash_get_help_us_enum(enum msg_hash_enums msg, char *s, size_t len) {
|
||||
"Increasing this value will increase \n"
|
||||
"performance, but introduce more latency.");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE:
|
||||
snprintf(s, len,
|
||||
"Whether to announce netplay games publicly. \n"
|
||||
" \n"
|
||||
"If set to false, clients must manually connect \n"
|
||||
"rather than using the public lobby.");
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_STATELESS_MODE:
|
||||
snprintf(s, len,
|
||||
"Whether to run netplay in a mode not requiring\n"
|
||||
|
@ -36,7 +36,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_NETPLAY_USERS_HAS_FLIPPED,
|
||||
"Netplay users has flipped"
|
||||
"Netplay users have flipped"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_SETTING_DISK_IN_TRAY,
|
||||
@ -112,7 +112,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_MENU_SETTINGS,
|
||||
"Adjusts settings related to the appearance of the menu screen."
|
||||
"Adjusts menu screen appearance settings."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC,
|
||||
@ -182,7 +182,6 @@ MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST,
|
||||
"Achievement List"
|
||||
)
|
||||
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_ACHIEVEMENT_LIST_HARDCORE,
|
||||
"Achievement List (Hardcore)"
|
||||
@ -449,7 +448,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CLOSE_CONTENT,
|
||||
"Close Application"
|
||||
"Close Content"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_CONFIG,
|
||||
@ -619,7 +618,7 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_CREATE,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_GAME_SPECIFIC_OPTIONS_IN_USE,
|
||||
"Game-options file")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP,
|
||||
"help")
|
||||
"Help")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_AUDIO_VIDEO_TROUBLESHOOTING,
|
||||
"Audio/Video Troubleshooting")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_HELP_CHANGE_VIRTUAL_GAMEPAD,
|
||||
@ -990,6 +989,8 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_NICKNAME,
|
||||
"Username")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PASSWORD,
|
||||
"Server Password")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
"Publicly Announce Netplay")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_SETTINGS,
|
||||
"Netplay settings")
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_STATELESS_MODE,
|
||||
@ -1717,41 +1718,41 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_TEST_UNOFFICIAL,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_HARDCORE_MODE_ENABLE,
|
||||
"Enable or disable savestates, cheats, rewind, fast-forward, pause, and slow-motion for all games.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_DRIVER_SETTINGS,
|
||||
"Change drivers for this system.")
|
||||
"Change drivers used by the system.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RETRO_ACHIEVEMENTS_SETTINGS,
|
||||
"Change settings for the achievements.")
|
||||
"Change achievement settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_SETTINGS,
|
||||
"Change settings for the core.")
|
||||
"Change core settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RECORDING_SETTINGS,
|
||||
"Change settings for the recording.")
|
||||
"Change recording settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONSCREEN_DISPLAY_SETTINGS,
|
||||
"Change settings for display overlay, keyboard overlay and onscreen notifications.")
|
||||
"Change display overlay and keyboard overlay, and onscreen notification settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_FRAME_THROTTLE_SETTINGS,
|
||||
"Change settings for rewinding, fast-forwarding, and slow-motion.")
|
||||
"Change rewind, fast-forward, and slow-motion settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAVING_SETTINGS,
|
||||
"Change settings for the saving.")
|
||||
"Change saving settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LOGGING_SETTINGS,
|
||||
"Change settings for the logging.")
|
||||
"Change logging settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_USER_INTERFACE_SETTINGS,
|
||||
"Change settings for the user interface.")
|
||||
"Change user interface settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_USER_SETTINGS,
|
||||
"Change accounts, username, and language.")
|
||||
"Change account, username, and language settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_PRIVACY_SETTINGS,
|
||||
"Change your privacy settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_DIRECTORY_SETTINGS,
|
||||
"Change default directories for this system.")
|
||||
"Change default directories where files are located.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_PLAYLIST_SETTINGS,
|
||||
"Change settings for the playlists.")
|
||||
"Change playlist settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_SETTINGS,
|
||||
"Configure server and network settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ADD_CONTENT_LIST,
|
||||
"Scan contents and add to the database.")
|
||||
"Scan content and add to the database.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_SETTINGS,
|
||||
"Adjusts settings for audio output.")
|
||||
"Change audio output settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_BLUETOOTH_ENABLE,
|
||||
"Enable or disable bluetooth.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CONFIG_SAVE_ON_EXIT,
|
||||
"Saves changes to configuration file on exit.")
|
||||
"Saves changes to the configuration file on exit.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATION_SETTINGS,
|
||||
"Change default settings for configuration files.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CONFIGURATIONS_LIST,
|
||||
@ -1765,7 +1766,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_HOTKEY_BINDS,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_MENU_ENUM_TOGGLE_GAMEPAD_COMBO,
|
||||
"Gamepad button combination to toggle menu.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_SETTINGS,
|
||||
"Adjusts settings for joypads, keyboard and mouse.")
|
||||
"Change joypad, keyboard, and mouse settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_USER_BINDS,
|
||||
"Configure controls for this user.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LOG_VERBOSITY,
|
||||
@ -1775,9 +1776,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_LAN_SCAN_SETTINGS,
|
||||
"Search for and connect to netplay hosts on the local network.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INFORMATION_LIST_LIST,
|
||||
"Display information for core, network, and system. Display manager for database and cursor.")
|
||||
"Display core, network, and system information.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_ONLINE_UPDATER,
|
||||
"Download add-ons, components and contents for RetroArch.")
|
||||
"Download add-ons, components, and content for RetroArch.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAMBA_ENABLE,
|
||||
"Enable or disable network sharing of your folders.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SERVICES_SETTINGS,
|
||||
@ -1789,13 +1790,13 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SSH_ENABLE,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SUSPEND_SCREENSAVER_ENABLE,
|
||||
"Prevents your system's screensaver from becoming active.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_WINDOW_SCALE,
|
||||
"Sets the window size relative to the core viewport size. Alternatively you can set a window width and height below for a fixed window size.")
|
||||
"Sets the window size relative to the core viewport size. Alternatively, you can set a window width and height below for a fixed window size.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_USER_LANGUAGE,
|
||||
"Sets the language of the interface.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_BLACK_FRAME_INSERTION,
|
||||
"Inserts a black frame inbetween frames. Useful for users of 120 Hz screens who want to play 60 Hz material with eliminated ghosting.")
|
||||
"Inserts a black frame inbetween frames. Useful for users with 120Hz screens who want to play 60Hz content to eliminate ghosting.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FRAME_DELAY,
|
||||
"Reduces latency at the cost of higher risk of video stuttering. Adds a delay after V-Sync (in ms).")
|
||||
"Reduces latency at the cost of a higher risk of video stuttering. Adds a delay after V-Sync (in ms).")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_HARD_SYNC_FRAMES,
|
||||
"Sets how many frames the CPU can run ahead of the GPU when using 'Hard GPU Sync'.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MAX_SWAPCHAIN_IMAGES,
|
||||
@ -1805,11 +1806,11 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MONITOR_INDEX,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_REFRESH_RATE_AUTO,
|
||||
"The accurate estimated refresh rate of the screen in Hz.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SETTINGS,
|
||||
"Adjusts settings for video output.")
|
||||
"Change video output settings.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_WIFI_SETTINGS,
|
||||
"Scans for wireless networks and establishes connection.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_HELP_LIST,
|
||||
"Learn more about how it works.")
|
||||
"Learn more about how the program works.")
|
||||
MSG_HASH(MSG_APPENDED_DISK,
|
||||
"Appended disk")
|
||||
MSG_HASH(MSG_APPLICATION_DIR,
|
||||
@ -1871,7 +1872,7 @@ MSG_HASH(MSG_COULD_NOT_READ_MOVIE_HEADER,
|
||||
MSG_HASH(MSG_COULD_NOT_READ_STATE_FROM_MOVIE,
|
||||
"Could not read state from movie.")
|
||||
MSG_HASH(MSG_CRC32_CHECKSUM_MISMATCH,
|
||||
"CRC32 checksum mismatch between content file and saved content checksum in replay file header) replay highly likely to desync on playback.")
|
||||
"CRC32 checksum mismatch between content file and saved content checksum in replay file header. Replay highly likely to desync on playback.")
|
||||
MSG_HASH(MSG_CUSTOM_TIMING_GIVEN,
|
||||
"Custom timing given")
|
||||
MSG_HASH(MSG_DECOMPRESSION_ALREADY_IN_PROGRESS,
|
||||
@ -2200,7 +2201,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_AUDIO_MUTE,
|
||||
"Mute/unmute audio.")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_RATE_CONTROL_DELTA,
|
||||
"Helps smooth out imperfections in timing when synchronizing audio and video at the same time. Be aware that if disabled, proper synchronization is nearly impossible to obtain."
|
||||
"Helps smooth out imperfections in timing when synchronizing audio and video. Be aware that if disabled, proper synchronization is nearly impossible to obtain."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CAMERA_ALLOW,
|
||||
@ -2224,7 +2225,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_VOLUME,
|
||||
"Audio volume (in dB). 0 dB is normal volume, no gain applied."
|
||||
"Audio volume (in dB). 0 dB is normal volume, and no gain is applied."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_SYNC,
|
||||
@ -2240,7 +2241,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_INPUT_TURBO_PERIOD,
|
||||
"Describes the period of which turbo-enabled buttons toggle. Numbers are described in frames."
|
||||
"Describes the period when turbo-enabled buttons are toggled. Numbers are described in frames."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_INPUT_DUTY_CYCLE,
|
||||
@ -2272,7 +2273,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_MAX_TIMING_SKEW,
|
||||
"The maximum change in audio input rate. You may want to increase this to enable very large changes in timing, for example running PAL cores on NTSC displays, at the cost of inaccurate audio pitch."
|
||||
"The maximum change in audio input rate. Increasing this enables very large changes in timing at the cost of an inaccurate audio pitch (e.g., running PAL cores on NTSC displays)."
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_FAILED,
|
||||
@ -2286,6 +2287,10 @@ MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
"not configured"
|
||||
)
|
||||
MSG_HASH(
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
"not configured, using fallback"
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_LABEL_VALUE_DATABASE_CURSOR_LIST,
|
||||
"Database Cursor List"
|
||||
@ -2346,7 +2351,7 @@ MSG_HASH(MSG_NETPLAY_LAN_SCAN_COMPLETE,
|
||||
MSG_HASH(MSG_NETPLAY_LAN_SCANNING,
|
||||
"Scanning for netplay hosts...")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_PAUSE_NONACTIVE,
|
||||
"Pause gameplay when window focus is lost.")
|
||||
"Pause gameplay when RetroArch is not the active window.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_DISABLE_COMPOSITION,
|
||||
"Enable or disable composition (Windows only).")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_HISTORY_LIST_ENABLE,
|
||||
@ -2396,9 +2401,9 @@ MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_ROOM_NICKNAME,
|
||||
MSG_HASH(MENU_ENUM_LABEL_VALUE_NETPLAY_COMPAT_CONTENT_FOUND,
|
||||
"Compatible content found")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_CROP_OVERSCAN,
|
||||
"Cuts off a few pixels around the edges of the image that were customarily left blank by developers and sometimes contain garbage pixels.")
|
||||
"Cuts off a few pixels around the edges of the image customarily left blank by developers which sometimes also contain garbage pixels.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SMOOTH,
|
||||
"Add a slight blur to the image to take the edge off of the hard pixel edges. This option has very little impact on performance.")
|
||||
"Adds a slight blur to the image to take the edge off of the hard pixel edges. This option has very little impact on performance.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FILTER,
|
||||
"Apply a CPU-powered video filter. NOTE: Might come at a high performance cost. Some video filters might only work for cores that use 32bit or 16bit color.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_USERNAME,
|
||||
@ -2406,13 +2411,13 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_USERNAME,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CHEEVOS_PASSWORD,
|
||||
"Input the password of your Retro Achievements account.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_NICKNAME,
|
||||
"Input your user name here. This will be used for netplay sessions among other things.")
|
||||
"Input your user name here. This will be used for netplay sessions, among other things.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_POST_FILTER_RECORD,
|
||||
"Capture the image after filters (but not shaders) are applied. Your video will look as fancy as what you see on your screen.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_LIST,
|
||||
"Select which application we wish to use.")
|
||||
"Select which core to use.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_CONTENT_LIST,
|
||||
"Select the content we want to start.")
|
||||
"Select which content to start.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETWORK_INFORMATION,
|
||||
"Show network interface(s) and associated IP addresses.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SYSTEM_INFORMATION,
|
||||
@ -2430,10 +2435,10 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_MESSAGE_POS_Y,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_FONT_SIZE,
|
||||
"Specify the font size in points.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_OVERLAY_HIDE_IN_MENU,
|
||||
"Hide the overlay while we are inside the menu, and show it again when exiting the menu.")
|
||||
"Hide the overlay while inside the menu, and show it again when exiting the menu.")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CONTENT_COLLECTION_LIST,
|
||||
"Content which has been scanned will appear here."
|
||||
"Scanned content will appear here."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_SCALE_INTEGER,
|
||||
@ -2465,7 +2470,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_INDEX,
|
||||
"When saving a savestate, save state index is automatically increased before it is saved. Also, when loading content, the index will be set to the highest existing index."
|
||||
"When making a savestate, save state index is automatically increased before it is saved. When loading content, the index will be set to the highest existing index."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_BLOCK_SRAM_OVERWRITE,
|
||||
@ -2473,11 +2478,11 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_FASTFORWARD_RATIO,
|
||||
"The maximum rate at which content will be run when using fast forward (E.g. 5x for 60 fps content => 300 fps cap). If this is set at 0x, then fastforward ratio is unlimited (no FPS cap)."
|
||||
"The maximum rate at which content will be run when using fast forward (e.g., 5.0x for 60 fps content = 300 fps cap). If set to 0.0x, fastforward ratio is unlimited (no FPS cap)."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SLOWMOTION_RATIO,
|
||||
"When slowmotion, content will slow down by a factor."
|
||||
"When in slow motion, content will slow down by the factor specified/set."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_REWIND_ENABLE,
|
||||
@ -2485,7 +2490,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_REWIND_GRANULARITY,
|
||||
"When rewinding defined number of frames, you can rewind several frames at a time, increasing the rewind speed."
|
||||
"When rewinding a defined number of frames, you can rewind several frames at a time, increasing the rewind speed."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_LIBRETRO_LOG_LEVEL,
|
||||
@ -2497,7 +2502,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_SAVE,
|
||||
"Automatically saves a savestate at the end of RetroArch's lifetime. RetroArch will automatically load this savestate if 'Savestate Auto Load' is set."
|
||||
"Automatically makes a savestate at the end of RetroArch's runtime. RetroArch will automatically load this savestate if 'Auto Load State' is enabled."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_SAVESTATE_AUTO_LOAD,
|
||||
@ -2525,7 +2530,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_PAUSE_LIBRETRO,
|
||||
"If disabled, the content will keep running in the background when we are in the menu."
|
||||
"If disabled, the content will keep running in the background when RetroArch's menu is toggled."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_VIDEO_DRIVER,
|
||||
@ -2589,7 +2594,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_AUDIO_OUTPUT_RATE,
|
||||
"Audio output samplerate."
|
||||
"Audio output sample rate."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_OVERLAY_OPACITY,
|
||||
@ -2619,6 +2624,10 @@ MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_PASSWORD,
|
||||
"The password for connecting to the netplay host. Used only in host mode."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
"Whether to announce netplay games publicly. If unset, clients must manually connect rather than using the public lobby."
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_NETPLAY_SPECTATE_PASSWORD,
|
||||
"The password for connecting to the netplay host with only spectator privileges. Used only in host mode."
|
||||
@ -2691,7 +2700,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_BUILDBOT_URL,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_BUILDBOT_ASSETS_URL,
|
||||
"URL to assets updater directory on the Libretro buildbot.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_UPDATER_AUTO_EXTRACT_ARCHIVE,
|
||||
"After downloading, automatically extract archives that the downloads are contained inside."
|
||||
"After downloading, automatically extract files contained in the downloaded archives."
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_REFRESH_ROOMS,
|
||||
"Scan for new rooms.")
|
||||
@ -2723,20 +2732,16 @@ MSG_HASH(MENU_ENUM_SUBLABEL_TAKE_SCREENSHOT,
|
||||
"Captures an image of the screen.")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CLOSE_CONTENT,
|
||||
#ifdef HAVE_DYNAMIC
|
||||
"Closes the current game and application. Any unsaved changes might be lost."
|
||||
#else
|
||||
"Closes the current game. Any unsaved changes might be lost."
|
||||
#endif
|
||||
"Closes the current content. Any unsaved changes might be lost."
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LOAD_STATE,
|
||||
"Load a saved state from the currently selected slot.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SAVE_STATE,
|
||||
"Save a state to the currently selected slot.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RESUME,
|
||||
"Resume the currently running application and leave the Quick Menu.")
|
||||
"Resume the currently running content and leave the Quick Menu.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RESUME_CONTENT,
|
||||
"Resume the currently running application and leave the Quick Menu.")
|
||||
"Resume the currently running content and leave the Quick Menu.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_STATE_SLOT,
|
||||
"Changes the currently selected state slot.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_UNDO_LOAD_STATE,
|
||||
@ -2749,7 +2754,7 @@ MSG_HASH(
|
||||
)
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_ACCOUNTS_LIST,
|
||||
"Manage currently configured accounts."
|
||||
"Manages currently configured accounts."
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_META_REWIND,
|
||||
"Manages rewind settings.")
|
||||
@ -2766,7 +2771,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_SHADER_OPTIONS,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_INPUT_REMAPPING_OPTIONS,
|
||||
"Change the controls for the currently running content.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_CORE_OPTIONS,
|
||||
"Change the options for the currently running application.")
|
||||
"Change the options for the currently running content.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_SHOW_ADVANCED_SETTINGS,
|
||||
"Show advanced settings for power users (hidden by default).")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_THREADED_DATA_RUNLOOP_ENABLE,
|
||||
@ -2782,14 +2787,14 @@ MSG_HASH(
|
||||
"Usually set by developers who bundle libretro/RetroArch apps to point to assets."
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_DYNAMIC_WALLPAPERS_DIRECTORY,
|
||||
"The place to store the wallpapers dynamically loaded by the menu depending on context.")
|
||||
"Directory to store wallpapers dynamically loaded by the menu depending on context.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_THUMBNAILS_DIRECTORY,
|
||||
"Supplementary thumbnails (boxarts/misc. images, etc.) are stored here."
|
||||
)
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_CONFIG_DIRECTORY,
|
||||
"Sets start directory for menu configuration browser.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_MIN,
|
||||
"The number of frames of input latency for netplay to use to hide network latency. This reduces jitter and makes netplay less CPU-intensive, at the expense of noticeable input lag.")
|
||||
"The number of frames of input latency for netplay to use to hide network latency. Reduces jitter and makes netplay less CPU-intensive, at the expense of noticeable input lag.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_NETPLAY_INPUT_LATENCY_FRAMES_RANGE,
|
||||
"The range of frames of input latency that may be used to hide network latency. Reduces jitter and makes netplay less CPU-intensive, at the expense of unpredictable input lag.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_DISK_CYCLE_TRAY_STATUS,
|
||||
@ -2829,7 +2834,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHOW_HISTORY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_XMB_SHOW_ADD,
|
||||
"Show the import content tab inside the main menu.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_RGUI_SHOW_START_SCREEN,
|
||||
"Show startup screen in menu. Is automatically set to false after we have started up the program for the first time.")
|
||||
"Show startup screen in menu. This is automatically set to false after the program starts for the first time.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_HEADER_OPACITY,
|
||||
"Modify the opacity of the header graphic.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_MATERIALUI_MENU_FOOTER_OPACITY,
|
||||
@ -2843,7 +2848,7 @@ MSG_HASH(MENU_ENUM_SUBLABEL_CORE_ASSETS_DIRECTORY,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_INPUT_REMAPPING_DIRECTORY,
|
||||
"Save all remapped controls to this directory.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_DIR_PATH,
|
||||
"A directory for where to search for applications/cores.")
|
||||
"Directory where the program searches for content/cores.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_LIBRETRO_INFO_PATH,
|
||||
"Application/core information files are stored here.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_JOYPAD_AUTOCONFIG_DIR,
|
||||
@ -2907,9 +2912,9 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_CORE,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_SAVE_GAME,
|
||||
"Save the current shader settings as the default settings for the content.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PARAMETERS,
|
||||
"Modifies current shader directly. Will not be saved to preset file.")
|
||||
"Modifies the current shader directly. Changes will not be saved to the preset file.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_SHADER_PRESET_PARAMETERS,
|
||||
"Modifies shader preset currently in menu.")
|
||||
"Modifies the shader preset itself currently used in the menu.")
|
||||
MSG_HASH(
|
||||
MENU_ENUM_SUBLABEL_CHEAT_NUM_PASSES,
|
||||
"Increase or decrease the amount of cheats."
|
||||
@ -2933,6 +2938,6 @@ MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_HEIGHT,
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_WIDTH,
|
||||
"Custom viewport width that is used if the Aspect Ratio is set to 'Custom'.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_X,
|
||||
"Custom viewport offset used for defining the X-axis position of the viewport. These are ignored if 'Scaled Integer' is enabled, it will be automatically centered then.")
|
||||
"Custom viewport offset used for defining the X-axis position of the viewport. These are ignored if 'Integer Scale' is enabled. It will be automatically centered then.")
|
||||
MSG_HASH(MENU_ENUM_SUBLABEL_VIDEO_VIEWPORT_CUSTOM_Y,
|
||||
"Custom viewport offset used for defining the Y-axis position of the viewport. These are ignored if 'Scaled Integer' is enabled, it will be automatically centered then.")
|
||||
"Custom viewport offset used for defining the Y-axis position of the viewport. These are ignored if 'Integer Scale' is enabled. It will be automatically centered then.")
|
||||
|
@ -41,6 +41,7 @@
|
||||
#define _WIN32_WINNT 0x0500 /*_WIN32_WINNT_WIN2K */
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#endif
|
||||
#elif defined(GEKKO)
|
||||
#include "gx_pthread.h"
|
||||
@ -81,16 +82,49 @@ struct sthread
|
||||
struct slock
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
HANDLE lock;
|
||||
CRITICAL_SECTION lock;
|
||||
#else
|
||||
pthread_mutex_t lock;
|
||||
#endif
|
||||
};
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* The syntax we'll use is mind-bending unless we use a struct. Plus, we might want to store more info later */
|
||||
/* This will be used as a linked list immplementing a queue of waiting threads */
|
||||
struct QueueEntry
|
||||
{
|
||||
struct QueueEntry *next;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct scond
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* With this implementation of scond, we don't have any way of waking
|
||||
* (or even identifying) specific threads
|
||||
* But we need to wake them in the order indicated by the queue.
|
||||
* This potato token will get get passed around every waiter.
|
||||
* The bearer can test whether he's next, and hold onto the potato if he is.
|
||||
* When he's done he can then put it back into play to progress
|
||||
* the queue further */
|
||||
HANDLE hot_potato;
|
||||
|
||||
/* The primary signalled event. Hot potatoes are passed until this is set. */
|
||||
HANDLE event;
|
||||
|
||||
/* the head of the queue; NULL if queue is empty */
|
||||
struct QueueEntry *head;
|
||||
|
||||
/* equivalent to the queue length */
|
||||
int waiters;
|
||||
|
||||
/* how many waiters in the queue have been conceptually wakened by signals
|
||||
* (even if we haven't managed to actually wake them yet) */
|
||||
int wakens;
|
||||
|
||||
/* used to control access to this scond, in case the user fails */
|
||||
CRITICAL_SECTION cs;
|
||||
|
||||
#else
|
||||
pthread_cond_t cond;
|
||||
#endif
|
||||
@ -167,7 +201,7 @@ error:
|
||||
* @thread : pointer to thread object
|
||||
*
|
||||
* Detach a thread. When a detached thread terminates, its
|
||||
* resource sare automatically released back to the system
|
||||
* resources are automatically released back to the system
|
||||
* without the need for another thread to join with the
|
||||
* terminated thread.
|
||||
*
|
||||
@ -214,6 +248,9 @@ void sthread_join(sthread_t *thread)
|
||||
*/
|
||||
bool sthread_isself(sthread_t *thread)
|
||||
{
|
||||
/* This thread can't possibly be a null thread */
|
||||
if (!thread) return false;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
return GetCurrentThread() == thread->thread;
|
||||
#else
|
||||
@ -231,23 +268,25 @@ bool sthread_isself(sthread_t *thread)
|
||||
**/
|
||||
slock_t *slock_new(void)
|
||||
{
|
||||
bool mutex_created = false;
|
||||
slock_t *lock = (slock_t*)calloc(1, sizeof(*lock));
|
||||
if (!lock)
|
||||
return NULL;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
lock->lock = CreateMutex(NULL, FALSE, NULL);
|
||||
if (!lock->lock)
|
||||
goto error;
|
||||
InitializeCriticalSection(&lock->lock);
|
||||
mutex_created = true;
|
||||
#else
|
||||
if ((pthread_mutex_init(&lock->lock, NULL) < 0))
|
||||
goto error;
|
||||
mutex_created = (pthread_mutex_init(&lock->lock, NULL) == 0);
|
||||
#endif
|
||||
|
||||
if (!mutex_created)
|
||||
goto error;
|
||||
|
||||
return lock;
|
||||
|
||||
error:
|
||||
slock_free(lock);
|
||||
free(lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -263,7 +302,7 @@ void slock_free(slock_t *lock)
|
||||
return;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
CloseHandle(lock->lock);
|
||||
DeleteCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_destroy(&lock->lock);
|
||||
#endif
|
||||
@ -283,7 +322,7 @@ void slock_lock(slock_t *lock)
|
||||
if (!lock)
|
||||
return;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
WaitForSingleObject(lock->lock, INFINITE);
|
||||
EnterCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_lock(&lock->lock);
|
||||
#endif
|
||||
@ -300,7 +339,7 @@ void slock_unlock(slock_t *lock)
|
||||
if (!lock)
|
||||
return;
|
||||
#ifdef USE_WIN32_THREADS
|
||||
ReleaseMutex(lock->lock);
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
#else
|
||||
pthread_mutex_unlock(&lock->lock);
|
||||
#endif
|
||||
@ -317,21 +356,55 @@ void slock_unlock(slock_t *lock)
|
||||
**/
|
||||
scond_t *scond_new(void)
|
||||
{
|
||||
bool event_created = false;
|
||||
scond_t *cond = (scond_t*)calloc(1, sizeof(*cond));
|
||||
|
||||
if (!cond)
|
||||
return NULL;
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
event_created = !!cond->event;
|
||||
#else
|
||||
event_created = (pthread_cond_init(&cond->cond, NULL) == 0);
|
||||
#endif
|
||||
|
||||
if (!event_created)
|
||||
/* This is very complex because recreating condition variable semantics
|
||||
* with Win32 parts is not easy.
|
||||
*
|
||||
* The main problem is that a condition variable can't be used to
|
||||
* "pre-wake" a thread (it will get wakened only after it's waited).
|
||||
*
|
||||
* Whereas a win32 event can pre-wake a thread (the event will be set
|
||||
* in advance, so a 'waiter' won't even have to wait on it).
|
||||
*
|
||||
* Keep in mind a condition variable can apparently pre-wake a thread,
|
||||
* insofar as spurious wakeups are always possible,
|
||||
* but nobody will be expecting this and it does not need to be simulated.
|
||||
*
|
||||
* Moreover, we won't be doing this, because it counts as a spurious wakeup
|
||||
* -- someone else with a genuine claim must get wakened, in any case.
|
||||
*
|
||||
* Therefore we choose to wake only one of the correct waiting threads.
|
||||
* So at the very least, we need to do something clever. But there's
|
||||
* bigger problems.
|
||||
* We don't even have a straightforward way in win32 to satisfy
|
||||
* pthread_cond_wait's atomicity requirement. The bulk of this
|
||||
* algorithm is solving that.
|
||||
*
|
||||
* Note: We might could simplify this using vista+ condition variables,
|
||||
* but we wanted an XP compatible solution. */
|
||||
cond->event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!cond->event) goto error;
|
||||
cond->hot_potato = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (!cond->hot_potato)
|
||||
{
|
||||
CloseHandle(cond->event);
|
||||
goto error;
|
||||
}
|
||||
|
||||
InitializeCriticalSection(&cond->cs);
|
||||
cond->waiters = cond->wakens = 0;
|
||||
cond->head = NULL;
|
||||
|
||||
#else
|
||||
if (pthread_cond_init(&cond->cond, NULL) != 0)
|
||||
goto error;
|
||||
#endif
|
||||
|
||||
return cond;
|
||||
|
||||
@ -353,12 +426,189 @@ void scond_free(scond_t *cond)
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
CloseHandle(cond->event);
|
||||
CloseHandle(cond->hot_potato);
|
||||
DeleteCriticalSection(&cond->cs);
|
||||
#else
|
||||
pthread_cond_destroy(&cond->cond);
|
||||
#endif
|
||||
free(cond);
|
||||
}
|
||||
|
||||
#ifdef USE_WIN32_THREADS
|
||||
static bool _scond_wait_win32(scond_t *cond, slock_t *lock, DWORD dwMilliseconds)
|
||||
{
|
||||
static bool beginPeriod = false;
|
||||
|
||||
struct QueueEntry myentry;
|
||||
struct QueueEntry **ptr;
|
||||
DWORD tsBegin;
|
||||
DWORD waitResult;
|
||||
DWORD dwFinalTimeout = dwMilliseconds; /* Careful! in case we begin in the head,
|
||||
we don't do the hot potato stuff,
|
||||
so this timeout needs presetting. */
|
||||
|
||||
/* Reminder: `lock` is held before this is called. */
|
||||
/* however, someone else may have called scond_signal without the lock. soo... */
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* since this library is meant for realtime game software
|
||||
* I have no problem setting this to 1 and forgetting about it. */
|
||||
if (!beginPeriod)
|
||||
{
|
||||
beginPeriod = true;
|
||||
timeBeginPeriod(1);
|
||||
}
|
||||
|
||||
/* Now we can take a good timestamp for use in faking the timeout ourselves. */
|
||||
/* But don't bother unless we need to (to save a little time) */
|
||||
if (dwMilliseconds != INFINITE)
|
||||
tsBegin = timeGetTime();
|
||||
|
||||
/* add ourselves to a queue of waiting threads */
|
||||
ptr = &cond->head;
|
||||
|
||||
/* walk to the end of the linked list */
|
||||
while (*ptr)
|
||||
ptr = &((*ptr)->next);
|
||||
|
||||
*ptr = &myentry;
|
||||
myentry.next = NULL;
|
||||
|
||||
cond->waiters++;
|
||||
|
||||
/* now the conceptual lock release and condition block are supposed to be atomic.
|
||||
* we can't do that in Windows, but we can simulate the effects by using
|
||||
* the queue, by the following analysis:
|
||||
* What happens if they aren't atomic?
|
||||
*
|
||||
* 1. a signaller can rush in and signal, expecting a waiter to get it;
|
||||
* but the waiter wouldn't, because he isn't blocked yet.
|
||||
* Solution: Win32 events make this easy. The event will sit there enabled
|
||||
*
|
||||
* 2. a signaller can rush in and signal, and then turn right around and wait.
|
||||
* Solution: the signaller will get queued behind the waiter, who's
|
||||
* enqueued before he releases the mutex. */
|
||||
|
||||
/* It's my turn if I'm the head of the queue.
|
||||
* Check to see if it's my turn. */
|
||||
while (cond->head != &myentry)
|
||||
{
|
||||
/* It isn't my turn: */
|
||||
DWORD timeout = INFINITE;
|
||||
|
||||
/* As long as someone is even going to be able to wake up
|
||||
* when they receive the potato, keep it going round. */
|
||||
if (cond->wakens > 0)
|
||||
SetEvent(cond->hot_potato);
|
||||
|
||||
/* Assess the remaining timeout time */
|
||||
if (dwMilliseconds != INFINITE)
|
||||
{
|
||||
DWORD now = timeGetTime();
|
||||
DWORD elapsed = now - tsBegin;
|
||||
|
||||
/* Try one last time with a zero timeout (keeps the code simpler) */
|
||||
if (elapsed > dwMilliseconds)
|
||||
elapsed = dwMilliseconds;
|
||||
|
||||
timeout = dwMilliseconds - elapsed;
|
||||
}
|
||||
|
||||
/* Let someone else go */
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Wait a while to catch the hot potato..
|
||||
* someone else should get a chance to go */
|
||||
/* After all, it isn't my turn (and it must be someone else's) */
|
||||
Sleep(0);
|
||||
waitResult = WaitForSingleObject(cond->hot_potato, timeout);
|
||||
|
||||
/* I should come out of here with the main lock taken */
|
||||
EnterCriticalSection(&lock->lock);
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
{
|
||||
/* Out of time! Now, let's think about this. I do have the potato now--
|
||||
* maybe it's my turn, and I have the event?
|
||||
* If that's the case, I could proceed right now without aborting
|
||||
* due to timeout.
|
||||
*
|
||||
* However.. I DID wait a real long time. The caller was willing
|
||||
* to wait that long.
|
||||
*
|
||||
* I choose to give him one last chance with a zero timeout
|
||||
* in the next step
|
||||
*/
|
||||
if (cond->head == &myentry)
|
||||
{
|
||||
dwFinalTimeout = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* It's not our turn and we're out of time. Give up.
|
||||
* Remove ourself from the queue and bail. */
|
||||
struct QueueEntry* curr = cond->head;
|
||||
|
||||
while (curr->next != &myentry)
|
||||
curr = curr->next;
|
||||
curr->next = myentry.next;
|
||||
cond->waiters--;
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* It's my turn now -- and I hold the potato */
|
||||
|
||||
/* I still have the main lock, in any case */
|
||||
/* I need to release it so that someone can set the event */
|
||||
LeaveCriticalSection(&lock->lock);
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Wait for someone to actually signal this condition */
|
||||
/* We're the only waiter waiting on the event right now -- everyone else
|
||||
* is waiting on something different */
|
||||
waitResult = WaitForSingleObject(cond->event, dwFinalTimeout);
|
||||
|
||||
/* Take the main lock so we can do work. Nobody else waits on this lock
|
||||
* for very long, so even though it's GO TIME we won't have to wait long */
|
||||
EnterCriticalSection(&lock->lock);
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* Remove ourselves from the queue */
|
||||
cond->head = myentry.next;
|
||||
cond->waiters--;
|
||||
|
||||
if (waitResult == WAIT_TIMEOUT)
|
||||
{
|
||||
/* Oops! ran out of time in the final wait. Just bail. */
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* If any other wakenings are pending, go ahead and set it up */
|
||||
/* There may actually be no waiters. That's OK. The first waiter will come in,
|
||||
* find it's his turn, and immediately get the signaled event */
|
||||
cond->wakens--;
|
||||
if (cond->wakens > 0)
|
||||
{
|
||||
SetEvent(cond->event);
|
||||
|
||||
/* Progress the queue: Put the hot potato back into play. It'll be
|
||||
* tossed around until next in line gets it */
|
||||
SetEvent(cond->hot_potato);
|
||||
}
|
||||
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* scond_wait:
|
||||
* @cond : pointer to condition variable object
|
||||
@ -369,10 +619,7 @@ void scond_free(scond_t *cond)
|
||||
void scond_wait(scond_t *cond, slock_t *lock)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
WaitForSingleObject(cond->event, 0);
|
||||
|
||||
SignalObjectAndWait(lock->lock, cond->event, INFINITE, FALSE);
|
||||
slock_lock(lock);
|
||||
_scond_wait_win32(cond, lock, INFINITE);
|
||||
#else
|
||||
pthread_cond_wait(&cond->cond, &lock->lock);
|
||||
#endif
|
||||
@ -388,9 +635,18 @@ void scond_wait(scond_t *cond, slock_t *lock)
|
||||
int scond_broadcast(scond_t *cond)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
/* FIXME _- check how this function should differ
|
||||
* from scond_signal implementation. */
|
||||
SetEvent(cond->event);
|
||||
/* remember: we currently have mutex */
|
||||
if (cond->waiters == 0)
|
||||
return 0;
|
||||
|
||||
/* awaken everything which is currently queued up */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
cond->wakens = cond->waiters;
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
|
||||
return 0;
|
||||
#else
|
||||
return pthread_cond_broadcast(&cond->cond);
|
||||
@ -407,7 +663,37 @@ int scond_broadcast(scond_t *cond)
|
||||
void scond_signal(scond_t *cond)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
SetEvent(cond->event);
|
||||
|
||||
/* Unfortunately, pthread_cond_signal does not require that the
|
||||
* lock be held in advance */
|
||||
/* To avoid stomping on the condvar from other threads, we need
|
||||
* to control access to it with this */
|
||||
EnterCriticalSection(&cond->cs);
|
||||
|
||||
/* remember: we currently have mutex */
|
||||
if (cond->waiters == 0)
|
||||
{
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
return;
|
||||
}
|
||||
|
||||
/* wake up the next thing in the queue */
|
||||
if (cond->wakens == 0)
|
||||
SetEvent(cond->event);
|
||||
|
||||
cond->wakens++;
|
||||
|
||||
/* The data structure is done being modified.. I think we can leave the CS now.
|
||||
* This would prevent some other thread from receiving the hot potato and then
|
||||
* immediately stalling for the critical section.
|
||||
* But remember, we were trying to replicate a semantic where this entire
|
||||
* scond_signal call was controlled (by the user) by a lock.
|
||||
* So in case there's trouble with this, we can move it after SetEvent() */
|
||||
LeaveCriticalSection(&cond->cs);
|
||||
|
||||
/* Since there is now at least one pending waken, the potato must be in play */
|
||||
SetEvent(cond->hot_potato);
|
||||
|
||||
#else
|
||||
pthread_cond_signal(&cond->cond);
|
||||
#endif
|
||||
@ -428,14 +714,29 @@ void scond_signal(scond_t *cond)
|
||||
bool scond_wait_timeout(scond_t *cond, slock_t *lock, int64_t timeout_us)
|
||||
{
|
||||
#ifdef USE_WIN32_THREADS
|
||||
DWORD ret;
|
||||
/* How to convert a microsecond (us) timeout to millisecond (ms)?
|
||||
*
|
||||
* Someone asking for a 0 timeout clearly wants immediate timeout.
|
||||
* Someone asking for a 1 timeout clearly wants an actual timeout
|
||||
* of the minimum length */
|
||||
|
||||
WaitForSingleObject(cond->event, 0);
|
||||
ret = SignalObjectAndWait(lock->lock, cond->event,
|
||||
(DWORD)(timeout_us) / 1000, FALSE);
|
||||
/* Someone asking for 1000 or 1001 timeout shouldn't
|
||||
* accidentally get 2ms. */
|
||||
DWORD dwMilliseconds = timeout_us/1000;
|
||||
|
||||
slock_lock(lock);
|
||||
return ret == WAIT_OBJECT_0;
|
||||
/* The implementation of a 0 timeout here with pthreads is sketchy.
|
||||
* It isn't clear what happens if pthread_cond_timedwait is called with NOW.
|
||||
* Moreover, it is possible that this thread gets pre-empted after the
|
||||
* clock_gettime but before the pthread_cond_timedwait.
|
||||
* In order to help smoke out problems caused by this strange usage,
|
||||
* let's treat a 0 timeout as always timing out.
|
||||
*/
|
||||
if (timeout_us == 0)
|
||||
return false;
|
||||
else if (timeout_us < 1000)
|
||||
dwMilliseconds = 1;
|
||||
|
||||
return _scond_wait_win32(cond,lock,dwMilliseconds);
|
||||
#else
|
||||
int ret;
|
||||
int64_t seconds, remainder;
|
||||
|
@ -30,7 +30,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../cheevos.h"
|
||||
#include "../cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#include "cheat_manager.h"
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "../menu_cbs.h"
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../../cheevos.h"
|
||||
#include "../../cheevos/cheevos.h"
|
||||
#endif
|
||||
#include "../../verbosity.h"
|
||||
|
||||
@ -184,6 +184,7 @@ default_sublabel_macro(action_bind_sublabel_overlay_opacity, MENU_
|
||||
default_sublabel_macro(action_bind_sublabel_overlay_scale, MENU_ENUM_SUBLABEL_OVERLAY_SCALE)
|
||||
default_sublabel_macro(action_bind_sublabel_overlay_enable, MENU_ENUM_SUBLABEL_INPUT_OVERLAY_ENABLE)
|
||||
default_sublabel_macro(action_bind_sublabel_overlay_preset, MENU_ENUM_SUBLABEL_OVERLAY_PRESET)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_public_announce, MENU_ENUM_SUBLABEL_NETPLAY_PUBLIC_ANNOUNCE)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_ip_address, MENU_ENUM_SUBLABEL_NETPLAY_IP_ADDRESS)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_tcp_udp_port, MENU_ENUM_SUBLABEL_NETPLAY_TCP_UDP_PORT)
|
||||
default_sublabel_macro(action_bind_sublabel_netplay_password, MENU_ENUM_SUBLABEL_NETPLAY_PASSWORD)
|
||||
@ -736,6 +737,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs,
|
||||
case MENU_ENUM_LABEL_STDIN_CMD_ENABLE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_stdin_cmd_enable);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_public_announce);
|
||||
break;
|
||||
case MENU_ENUM_LABEL_NETPLAY_NAT_TRAVERSAL:
|
||||
BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_netplay_nat_traversal);
|
||||
break;
|
||||
|
@ -184,7 +184,7 @@ static void mui_context_reset_textures(mui_handle_t *mui)
|
||||
APPLICATION_SPECIAL_DIRECTORY_ASSETS_MATERIALUI_ICONS);
|
||||
|
||||
for (i = 0; i < MUI_TEXTURE_LAST; i++)
|
||||
menu_display_reset_textures_list(mui_texture_path(i), iconpath, &mui->textures.list[i]);
|
||||
menu_display_reset_textures_list(mui_texture_path(i), iconpath, &mui->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
static void mui_draw_icon(
|
||||
|
@ -3467,7 +3467,7 @@ static void xmb_context_reset_textures(
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < XMB_TEXTURE_LAST; i++)
|
||||
menu_display_reset_textures_list(xmb_texture_path(i), iconpath, &xmb->textures.list[i]);
|
||||
menu_display_reset_textures_list(xmb_texture_path(i), iconpath, &xmb->textures.list[i], TEXTURE_FILTER_MIPMAP_LINEAR);
|
||||
|
||||
menu_display_allocate_white_texture();
|
||||
|
||||
|
@ -500,7 +500,7 @@ void menu_display_draw_pipeline(menu_display_ctx_draw_t *draw)
|
||||
menu_disp->draw_pipeline(draw);
|
||||
}
|
||||
|
||||
void menu_display_draw_bg(menu_display_ctx_draw_t *draw,
|
||||
void menu_display_draw_bg(menu_display_ctx_draw_t *draw,
|
||||
video_frame_info_t *video_info, bool add_opacity_to_wallpaper)
|
||||
{
|
||||
static struct video_coords coords;
|
||||
@ -611,6 +611,281 @@ void menu_display_draw_texture(
|
||||
menu_display_draw(&draw);
|
||||
}
|
||||
|
||||
/* Draw the texture split into 9 sections, without scaling the corners.
|
||||
* The middle sections will only scale in the X axis, and the side
|
||||
* sections will only scale in the Y axis. */
|
||||
void menu_display_draw_texture_slice(
|
||||
int x, int y, unsigned w, unsigned h,
|
||||
unsigned new_w, unsigned new_h,
|
||||
unsigned width, unsigned height,
|
||||
float *color, unsigned offset, float scale_factor, uintptr_t texture)
|
||||
{
|
||||
menu_display_ctx_draw_t draw;
|
||||
menu_display_ctx_rotate_draw_t rotate_draw;
|
||||
struct video_coords coords;
|
||||
math_matrix_4x4 mymat;
|
||||
unsigned i;
|
||||
|
||||
/* need space for the coordinates of two triangles in a strip, so 8 vertices */
|
||||
float *tex_coord = (float*)malloc(8 * sizeof(float));
|
||||
float *vert_coord = (float*)malloc(8 * sizeof(float));
|
||||
float *colors = (float*)malloc(16 * sizeof(float));
|
||||
|
||||
/* normalized width/height of the amount to offset from the corners,
|
||||
* for both the vertex and texture coordinates */
|
||||
float vert_woff = (offset * scale_factor) / (float)width;
|
||||
float vert_hoff = (offset * scale_factor) / (float)height;
|
||||
float tex_woff = offset / (float)w;
|
||||
float tex_hoff = offset / (float)h;
|
||||
|
||||
/* the width/height of the middle sections of both the scaled and original image */
|
||||
float vert_scaled_mid_width = (new_w - (offset * scale_factor * 2)) / (float)width;
|
||||
float vert_scaled_mid_height = (new_h - (offset * scale_factor * 2)) / (float)height;
|
||||
float tex_mid_width = (w - (offset * 2)) / (float)w;
|
||||
float tex_mid_height = (h - (offset * 2)) / (float)h;
|
||||
|
||||
/* normalized coordinates for the start position of the image */
|
||||
float norm_x = x / (float)width;
|
||||
float norm_y = (height - y) / (float)height;
|
||||
|
||||
/* the four vertices of the top-left corner of the image,
|
||||
* used as a starting point for all the other sections */
|
||||
float V_BL[2] = {norm_x, norm_y};
|
||||
float V_BR[2] = {norm_x + vert_woff, norm_y};
|
||||
float V_TL[2] = {norm_x, norm_y + vert_hoff};
|
||||
float V_TR[2] = {norm_x + vert_woff, norm_y + vert_hoff};
|
||||
float T_BL[2] = {0.0f, tex_hoff};
|
||||
float T_BR[2] = {tex_woff, tex_hoff};
|
||||
float T_TL[2] = {0.0f, 0.0f};
|
||||
float T_TR[2] = {tex_woff, 0.0f};
|
||||
|
||||
for (i = 0; i < (16 * sizeof(float)) / sizeof(colors[0]); i++)
|
||||
colors[i] = 1.0f;
|
||||
|
||||
rotate_draw.matrix = &mymat;
|
||||
rotate_draw.rotation = 0.0;
|
||||
rotate_draw.scale_x = 1.0;
|
||||
rotate_draw.scale_y = 1.0;
|
||||
rotate_draw.scale_z = 1;
|
||||
rotate_draw.scale_enable = true;
|
||||
coords.vertices = 4;
|
||||
coords.vertex = vert_coord;
|
||||
coords.tex_coord = tex_coord;
|
||||
coords.lut_tex_coord = NULL;
|
||||
draw.width = width;
|
||||
draw.height = height;
|
||||
draw.coords = &coords;
|
||||
draw.matrix_data = &mymat;
|
||||
draw.prim_type = MENU_DISPLAY_PRIM_TRIANGLESTRIP;
|
||||
draw.pipeline.id = 0;
|
||||
coords.color = (const float*)colors;
|
||||
|
||||
menu_display_rotate_z(&rotate_draw);
|
||||
|
||||
draw.texture = texture;
|
||||
draw.x = 0;
|
||||
draw.y = 0;
|
||||
|
||||
/* vertex coords are specfied bottom-up in this order: BL BR TL TR */
|
||||
/* texture coords are specfied top-down in this order: BL BR TL TR */
|
||||
|
||||
/* If someone wants to change this to not draw several times, the
|
||||
* coordinates will need to be modified because of the triangle strip usage. */
|
||||
|
||||
/* top-left corner */
|
||||
vert_coord[0] = V_BL[0];
|
||||
vert_coord[1] = V_BL[1];
|
||||
vert_coord[2] = V_BR[0];
|
||||
vert_coord[3] = V_BR[1];
|
||||
vert_coord[4] = V_TL[0];
|
||||
vert_coord[5] = V_TL[1];
|
||||
vert_coord[6] = V_TR[0];
|
||||
vert_coord[7] = V_TR[1];
|
||||
|
||||
tex_coord[0] = T_BL[0];
|
||||
tex_coord[1] = T_BL[1];
|
||||
tex_coord[2] = T_BR[0];
|
||||
tex_coord[3] = T_BR[1];
|
||||
tex_coord[4] = T_TL[0];
|
||||
tex_coord[5] = T_TL[1];
|
||||
tex_coord[6] = T_TR[0];
|
||||
tex_coord[7] = T_TR[1];
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* top-middle section */
|
||||
vert_coord[0] = V_BL[0] + vert_woff;
|
||||
vert_coord[1] = V_BL[1];
|
||||
vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
|
||||
vert_coord[3] = V_BR[1];
|
||||
vert_coord[4] = V_TL[0] + vert_woff;
|
||||
vert_coord[5] = V_TL[1];
|
||||
vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
|
||||
vert_coord[7] = V_TR[1];
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff;
|
||||
tex_coord[1] = T_BL[1];
|
||||
tex_coord[2] = T_BR[0] + tex_mid_width;
|
||||
tex_coord[3] = T_BR[1];
|
||||
tex_coord[4] = T_TL[0] + tex_woff;
|
||||
tex_coord[5] = T_TL[1];
|
||||
tex_coord[6] = T_TR[0] + tex_mid_width;
|
||||
tex_coord[7] = T_TR[1];
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* top-right corner */
|
||||
vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[1] = V_BL[1];
|
||||
vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff;
|
||||
vert_coord[3] = V_BR[1];
|
||||
vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[5] = V_TL[1];
|
||||
vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff;
|
||||
vert_coord[7] = V_TR[1];
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[1] = T_BL[1];
|
||||
tex_coord[2] = T_BR[0] + tex_mid_width + tex_woff;
|
||||
tex_coord[3] = T_BR[1];
|
||||
tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[5] = T_TL[1];
|
||||
tex_coord[6] = T_TR[0] + tex_mid_width + tex_woff;
|
||||
tex_coord[7] = T_TR[1];
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* middle-left section */
|
||||
vert_coord[0] = V_BL[0];
|
||||
vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0];
|
||||
vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0];
|
||||
vert_coord[5] = V_TL[1] - vert_hoff;
|
||||
vert_coord[6] = V_TR[0];
|
||||
vert_coord[7] = V_TR[1] - vert_hoff;
|
||||
|
||||
tex_coord[0] = T_BL[0];
|
||||
tex_coord[1] = T_BL[1] + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0];
|
||||
tex_coord[3] = T_BR[1] + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0];
|
||||
tex_coord[5] = T_TL[1] + tex_hoff;
|
||||
tex_coord[6] = T_TR[0];
|
||||
tex_coord[7] = T_TR[1] + tex_hoff;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* center section */
|
||||
vert_coord[0] = V_BL[0] + vert_woff;
|
||||
vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
|
||||
vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0] + vert_woff;
|
||||
vert_coord[5] = V_TL[1] - vert_hoff;
|
||||
vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
|
||||
vert_coord[7] = V_TR[1] - vert_hoff;
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff;
|
||||
tex_coord[1] = T_BL[1] + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0] + tex_mid_width;
|
||||
tex_coord[3] = T_BR[1] + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0] + tex_woff;
|
||||
tex_coord[5] = T_TL[1] + tex_hoff;
|
||||
tex_coord[6] = T_TR[0] + tex_mid_width;
|
||||
tex_coord[7] = T_TR[1] + tex_hoff;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* middle-right section */
|
||||
vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[1] = V_BL[1] - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[3] = V_BR[1] - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[5] = V_TL[1] - vert_hoff;
|
||||
vert_coord[6] = V_TR[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[7] = V_TR[1] - vert_hoff;
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[1] = T_BL[1] + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[3] = T_BR[1] + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[5] = T_TL[1] + tex_hoff;
|
||||
tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[7] = T_TR[1] + tex_hoff;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* bottom-left corner */
|
||||
vert_coord[0] = V_BL[0];
|
||||
vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0];
|
||||
vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0];
|
||||
vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[6] = V_TR[0];
|
||||
vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height;
|
||||
|
||||
tex_coord[0] = T_BL[0];
|
||||
tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0];
|
||||
tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0];
|
||||
tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[6] = T_TR[0];
|
||||
tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* bottom-middle section */
|
||||
vert_coord[0] = V_BL[0] + vert_woff;
|
||||
vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0] + vert_scaled_mid_width;
|
||||
vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0] + vert_woff;
|
||||
vert_coord[5] = V_TL[1] - vert_scaled_mid_height;
|
||||
vert_coord[6] = V_TR[0] + vert_scaled_mid_width;
|
||||
vert_coord[7] = V_TR[1] - vert_scaled_mid_height;
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff;
|
||||
tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0] + tex_mid_width;
|
||||
tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0] + tex_woff;
|
||||
tex_coord[5] = T_TL[1] + tex_mid_height;
|
||||
tex_coord[6] = T_TR[0] + tex_mid_width;
|
||||
tex_coord[7] = T_TR[1] + tex_mid_height;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
/* bottom-right corner */
|
||||
vert_coord[0] = V_BL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[1] = V_BL[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[2] = V_BR[0] + vert_scaled_mid_width + vert_woff;
|
||||
vert_coord[3] = V_BR[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[4] = V_TL[0] + vert_woff + vert_scaled_mid_width;
|
||||
vert_coord[5] = V_TL[1] - vert_hoff - vert_scaled_mid_height;
|
||||
vert_coord[6] = V_TR[0] + vert_scaled_mid_width + vert_woff;
|
||||
vert_coord[7] = V_TR[1] - vert_hoff - vert_scaled_mid_height;
|
||||
|
||||
tex_coord[0] = T_BL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[1] = T_BL[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[2] = T_BR[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[3] = T_BR[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[4] = T_TL[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[5] = T_TL[1] + tex_hoff + tex_mid_height;
|
||||
tex_coord[6] = T_TR[0] + tex_woff + tex_mid_width;
|
||||
tex_coord[7] = T_TR[1] + tex_hoff + tex_mid_height;
|
||||
|
||||
menu_display_draw(&draw);
|
||||
|
||||
free(colors);
|
||||
free(vert_coord);
|
||||
free(tex_coord);
|
||||
}
|
||||
|
||||
void menu_display_rotate_z(menu_display_ctx_rotate_draw_t *draw)
|
||||
{
|
||||
#if !defined(VITA)
|
||||
@ -907,7 +1182,7 @@ void menu_display_set_alpha(float *color, float alpha_value)
|
||||
}
|
||||
|
||||
void menu_display_reset_textures_list(const char *texture_path, const char *iconpath,
|
||||
uintptr_t *item)
|
||||
uintptr_t *item, enum texture_filter_type filter_type)
|
||||
{
|
||||
struct texture_image ti;
|
||||
char path[PATH_MAX_LENGTH];
|
||||
@ -929,6 +1204,6 @@ void menu_display_reset_textures_list(const char *texture_path, const char *icon
|
||||
return;
|
||||
|
||||
video_driver_texture_load(&ti,
|
||||
TEXTURE_FILTER_MIPMAP_LINEAR, item);
|
||||
filter_type, item);
|
||||
image_texture_free(&ti);
|
||||
}
|
||||
|
@ -256,6 +256,9 @@ void menu_display_draw_quad(int x, int y, unsigned w, unsigned h,
|
||||
void menu_display_draw_texture(int x, int y, unsigned w, unsigned h,
|
||||
unsigned width, unsigned height,
|
||||
float *color, uintptr_t texture);
|
||||
void menu_display_draw_texture_slice(int x, int y, unsigned w, unsigned h,
|
||||
unsigned new_w, unsigned new_h, unsigned width, unsigned height,
|
||||
float *color, unsigned offset, float scale_factor, uintptr_t texture);
|
||||
void menu_display_rotate_z(menu_display_ctx_rotate_draw_t *draw);
|
||||
bool menu_display_get_tex_coords(menu_display_ctx_coord_draw_t *draw);
|
||||
|
||||
@ -294,7 +297,7 @@ void menu_display_set_alpha(float *color, float alpha_value);
|
||||
font_data_t *menu_display_font(enum application_special_type type, float font_size);
|
||||
|
||||
void menu_display_reset_textures_list(const char *texture_path, const char *iconpath,
|
||||
uintptr_t *item);
|
||||
uintptr_t *item, enum texture_filter_type filter_type);
|
||||
|
||||
extern uintptr_t menu_display_white_texture;
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../cheevos.h"
|
||||
#include "../cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_NETWORKING
|
||||
@ -4793,6 +4793,10 @@ bool menu_displaylist_ctl(enum menu_displaylist_ctl_state type, void *data)
|
||||
unsigned user;
|
||||
unsigned count = 0;
|
||||
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
PARSE_ONLY_BOOL, false) != -1)
|
||||
count++;
|
||||
if (menu_displaylist_parse_settings_enum(menu, info,
|
||||
MENU_ENUM_LABEL_NETPLAY_IP_ADDRESS,
|
||||
PARSE_ONLY_STRING, false) != -1)
|
||||
|
@ -2817,13 +2817,13 @@ static bool setting_append_list(
|
||||
bool_entries[3].name_enum_idx = MENU_ENUM_LABEL_AUTO_OVERRIDES_ENABLE;
|
||||
bool_entries[3].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_AUTO_OVERRIDES_ENABLE;
|
||||
bool_entries[3].default_value = default_auto_overrides_enable;
|
||||
bool_entries[3].flags = SD_FLAG_NONE;
|
||||
bool_entries[3].flags = SD_FLAG_ADVANCED;
|
||||
|
||||
bool_entries[4].target = &settings->auto_remaps_enable;
|
||||
bool_entries[4].name_enum_idx = MENU_ENUM_LABEL_AUTO_REMAPS_ENABLE;
|
||||
bool_entries[4].SHORT_enum_idx = MENU_ENUM_LABEL_VALUE_AUTO_REMAPS_ENABLE;
|
||||
bool_entries[4].default_value = default_auto_remaps_enable;
|
||||
bool_entries[4].flags = SD_FLAG_NONE;
|
||||
bool_entries[4].flags = SD_FLAG_ADVANCED;
|
||||
|
||||
bool_entries[5].target = &settings->auto_shaders_enable;
|
||||
bool_entries[5].name_enum_idx = MENU_ENUM_LABEL_AUTO_SHADERS_ENABLE;
|
||||
@ -5590,6 +5590,21 @@ static bool setting_append_list(
|
||||
#if defined(HAVE_NETWORK_CMD)
|
||||
unsigned user;
|
||||
#endif
|
||||
CONFIG_BOOL(
|
||||
list, list_info,
|
||||
&settings->netplay.public_announce,
|
||||
MENU_ENUM_LABEL_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
MENU_ENUM_LABEL_VALUE_NETPLAY_PUBLIC_ANNOUNCE,
|
||||
true,
|
||||
MENU_ENUM_LABEL_VALUE_OFF,
|
||||
MENU_ENUM_LABEL_VALUE_ON,
|
||||
&group_info,
|
||||
&subgroup_info,
|
||||
parent_group,
|
||||
general_write_handler,
|
||||
general_read_handler,
|
||||
SD_FLAG_NONE);
|
||||
|
||||
CONFIG_STRING(
|
||||
list, list_info,
|
||||
settings->netplay.server,
|
||||
|
@ -22,7 +22,7 @@
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CHEEVOS
|
||||
#include "../../cheevos.h"
|
||||
#include "../../cheevos/cheevos.h"
|
||||
#endif
|
||||
|
||||
#include "menu_dialog.h"
|
||||
|
@ -168,6 +168,7 @@ enum msg_hash_enums
|
||||
MSG_CAPABILITIES,
|
||||
MSG_DEVICE_CONFIGURED_IN_PORT,
|
||||
MSG_DEVICE_NOT_CONFIGURED,
|
||||
MSG_DEVICE_NOT_CONFIGURED_FALLBACK,
|
||||
MSG_DEVICE_DISCONNECTED_FROM_PORT,
|
||||
MSG_NO_ARGUMENTS_SUPPLIED_AND_NO_MENU_BUILTIN,
|
||||
MSG_COMPILER,
|
||||
@ -1017,6 +1018,7 @@ enum msg_hash_enums
|
||||
MENU_LABEL(BLUETOOTH_ENABLE),
|
||||
MENU_LABEL(NETPLAY_CLIENT_SWAP_INPUT),
|
||||
MENU_LABEL(NETPLAY_DELAY_FRAMES),
|
||||
MENU_LABEL(NETPLAY_PUBLIC_ANNOUNCE),
|
||||
MENU_LABEL(NETPLAY_STATELESS_MODE),
|
||||
MENU_LABEL(NETPLAY_CHECK_FRAMES),
|
||||
MENU_LABEL(NETPLAY_INPUT_LATENCY_FRAMES_MIN),
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include "../../core.h"
|
||||
#include "../../gfx/video_driver.h"
|
||||
#include "../../managers/core_option_manager.h"
|
||||
#include "../../cheevos.h"
|
||||
#include "../../cheevos/cheevos.h"
|
||||
#include "../../content.h"
|
||||
|
||||
#define BASIC_INFO "info"
|
||||
|
@ -316,6 +316,14 @@ Payload:
|
||||
Description:
|
||||
Request that a client stall for the given number of frames.
|
||||
|
||||
Command: RESET
|
||||
Payload:
|
||||
{
|
||||
frame: uint32
|
||||
}
|
||||
Description:
|
||||
Indicate that the core was reset at the beginning of the given frame.
|
||||
|
||||
Command: CHEATS
|
||||
Unused
|
||||
|
||||
|
@ -44,6 +44,7 @@ enum rarch_netplay_ctl_state
|
||||
RARCH_NETPLAY_CTL_PAUSE,
|
||||
RARCH_NETPLAY_CTL_UNPAUSE,
|
||||
RARCH_NETPLAY_CTL_LOAD_SAVESTATE,
|
||||
RARCH_NETPLAY_CTL_RESET,
|
||||
RARCH_NETPLAY_CTL_DISCONNECT
|
||||
};
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "../../tasks/tasks_internal.h"
|
||||
#include <file/file_path.h>
|
||||
#include "../../file_path_special.h"
|
||||
#include "paths.h"
|
||||
#include "../../paths.h"
|
||||
|
||||
/* Only used before init_netplay */
|
||||
static bool netplay_enabled = false;
|
||||
@ -652,11 +652,22 @@ static void netplay_frontend_paused(netplay_t *netplay, bool paused)
|
||||
bool netplay_pre_frame(netplay_t *netplay)
|
||||
{
|
||||
bool sync_stalled;
|
||||
reannounce ++;
|
||||
if (netplay->is_server && (reannounce % 3600 == 0))
|
||||
netplay_announce();
|
||||
settings_t *settings = config_get_ptr();
|
||||
|
||||
retro_assert(netplay);
|
||||
|
||||
if (settings->netplay.public_announce)
|
||||
{
|
||||
reannounce ++;
|
||||
if (netplay->is_server && (reannounce % 3600 == 0))
|
||||
netplay_announce();
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Make sure that if announcement is turned on mid-game, it gets announced */
|
||||
reannounce = -1;
|
||||
}
|
||||
|
||||
/* FIXME: This is an ugly way to learn we're not paused anymore */
|
||||
if (netplay->local_paused)
|
||||
netplay_frontend_paused(netplay, false);
|
||||
@ -729,6 +740,48 @@ void netplay_post_frame(netplay_t *netplay)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_force_future
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Force netplay to ignore all past input, typically because we've just loaded
|
||||
* a state or reset.
|
||||
*/
|
||||
static void netplay_force_future(netplay_t *netplay)
|
||||
{
|
||||
/* Wherever we're inputting, that's where we consider our state to be loaded */
|
||||
netplay->run_ptr = netplay->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
netplay_update_unread_ptr(netplay);
|
||||
if (netplay->unread_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
uint32_t player;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] < netplay->run_frame_count)
|
||||
{
|
||||
netplay->read_ptr[player] = netplay->run_ptr;
|
||||
netplay->read_frame_count[player] = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
if (netplay->server_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->server_ptr = netplay->run_ptr;
|
||||
netplay->server_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
netplay_update_unread_ptr(netplay);
|
||||
}
|
||||
if (netplay->other_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_send_savestate
|
||||
* @netplay : pointer to netplay object
|
||||
@ -797,10 +850,7 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
{
|
||||
retro_ctx_serialize_info_t tmp_serial_info;
|
||||
|
||||
/* Wherever we're inputting, that's where we consider our state to be loaded
|
||||
* (FIXME: Need to be more careful about saving it?) */
|
||||
netplay->run_ptr = netplay->self_ptr;
|
||||
netplay->run_frame_count = netplay->self_frame_count;
|
||||
netplay_force_future(netplay);
|
||||
|
||||
/* Record it in our own buffer */
|
||||
if (save || !serial_info)
|
||||
@ -833,34 +883,6 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to ignore any intervening data from the other side,
|
||||
* and never rewind past this */
|
||||
netplay_update_unread_ptr(netplay);
|
||||
if (netplay->unread_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
uint32_t player;
|
||||
for (player = 0; player < MAX_USERS; player++)
|
||||
{
|
||||
if (!(netplay->connected_players & (1<<player))) continue;
|
||||
if (netplay->read_frame_count[player] < netplay->run_frame_count)
|
||||
{
|
||||
netplay->read_ptr[player] = netplay->run_ptr;
|
||||
netplay->read_frame_count[player] = netplay->run_frame_count;
|
||||
}
|
||||
}
|
||||
if (netplay->server_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->server_ptr = netplay->run_ptr;
|
||||
netplay->server_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
netplay_update_unread_ptr(netplay);
|
||||
}
|
||||
if (netplay->other_frame_count < netplay->run_frame_count)
|
||||
{
|
||||
netplay->other_ptr = netplay->run_ptr;
|
||||
netplay->other_frame_count = netplay->run_frame_count;
|
||||
}
|
||||
|
||||
/* If we can't send it to the peer, loading a state was a bad idea */
|
||||
if (netplay->quirks & (
|
||||
NETPLAY_QUIRK_NO_SAVESTATES
|
||||
@ -875,6 +897,37 @@ void netplay_load_savestate(netplay_t *netplay,
|
||||
&netplay->compress_zlib);
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_core_reset
|
||||
* @netplay : pointer to netplay object
|
||||
*
|
||||
* Indicate that the core has been reset to netplay peers
|
||||
**/
|
||||
static void netplay_core_reset(netplay_t *netplay)
|
||||
{
|
||||
uint32_t cmd[3];
|
||||
size_t i;
|
||||
|
||||
/* Ignore past input */
|
||||
netplay_force_future(netplay);
|
||||
|
||||
/* Request that our peers reset */
|
||||
cmd[0] = htonl(NETPLAY_CMD_RESET);
|
||||
cmd[1] = htonl(sizeof(uint32_t));
|
||||
cmd[2] = htonl(netplay->self_frame_count);
|
||||
|
||||
for (i = 0; i < netplay->connections_size; i++)
|
||||
{
|
||||
struct netplay_connection *connection = &netplay->connections[i];
|
||||
if (!connection->active ||
|
||||
connection->mode < NETPLAY_CONNECTION_CONNECTED) continue;
|
||||
|
||||
if (!netplay_send(&connection->send_packet_buffer, connection->fd, cmd,
|
||||
sizeof(cmd)))
|
||||
netplay_hangup(netplay, connection);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* netplay_toggle_play_spectate
|
||||
*
|
||||
@ -1027,7 +1080,8 @@ bool init_netplay(void *direct_host, const char *server, unsigned port)
|
||||
msg_hash_to_str(MSG_WAITING_FOR_CLIENT),
|
||||
0, 180, false);
|
||||
|
||||
netplay_announce();
|
||||
if (settings->netplay.public_announce)
|
||||
netplay_announce();
|
||||
}
|
||||
|
||||
netplay_data = (netplay_t*)netplay_new(
|
||||
@ -1133,6 +1187,9 @@ bool netplay_driver_ctl(enum rarch_netplay_ctl_state state, void *data)
|
||||
case RARCH_NETPLAY_CTL_LOAD_SAVESTATE:
|
||||
netplay_load_savestate(netplay_data, (retro_ctx_serialize_info_t*)data, true);
|
||||
break;
|
||||
case RARCH_NETPLAY_CTL_RESET:
|
||||
netplay_core_reset(netplay_data);
|
||||
break;
|
||||
case RARCH_NETPLAY_CTL_DISCONNECT:
|
||||
ret = netplay_disconnect(netplay_data);
|
||||
goto done;
|
||||
|
@ -25,13 +25,20 @@
|
||||
|
||||
#include "netplay_private.h"
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../../config.h"
|
||||
#endif
|
||||
|
||||
#include "../../autosave.h"
|
||||
#include "../../configuration.h"
|
||||
#include "../../content.h"
|
||||
#include "../../retroarch.h"
|
||||
#include "../../runloop.h"
|
||||
#include "../../version.h"
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
#include "../../menu/widgets/menu_input_dialog.h"
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_SOCKET_LEGACY
|
||||
/* Custom inet_ntop. Win32 doesn't seem to support this ... */
|
||||
@ -265,8 +272,9 @@ struct info_buf_s
|
||||
} \
|
||||
else if (recvd < 0)
|
||||
|
||||
static netplay_t *handshake_password_netplay;
|
||||
static netplay_t *handshake_password_netplay = NULL;
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
static void handshake_password(void *ignore, const char *line)
|
||||
{
|
||||
struct password_buf_s password_buf;
|
||||
@ -285,9 +293,12 @@ static void handshake_password(void *ignore, const char *line)
|
||||
if (netplay_send(&connection->send_packet_buffer, connection->fd, &password_buf, sizeof(password_buf)))
|
||||
netplay_send_flush(&connection->send_packet_buffer, connection->fd, false);
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
menu_input_dialog_end();
|
||||
rarch_ctl(RARCH_CTL_MENU_RUNNING_FINISHED, NULL);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* netplay_handshake_init
|
||||
@ -381,15 +392,21 @@ bool netplay_handshake_init(netplay_t *netplay,
|
||||
/* If a password is demanded, ask for it */
|
||||
if (!netplay->is_server && (connection->salt = ntohl(header[3])))
|
||||
{
|
||||
#ifdef HAVE_MENU
|
||||
menu_input_ctx_line_t line;
|
||||
rarch_ctl(RARCH_CTL_MENU_RUNNING, NULL);
|
||||
memset(&line, 0, sizeof(line));
|
||||
#endif
|
||||
|
||||
handshake_password_netplay = netplay;
|
||||
line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD);
|
||||
|
||||
#ifdef HAVE_MENU
|
||||
memset(&line, 0, sizeof(line));
|
||||
line.label = msg_hash_to_str(MSG_NETPLAY_ENTER_PASSWORD);
|
||||
line.label_setting = "no_setting";
|
||||
line.cb = handshake_password;
|
||||
line.cb = handshake_password;
|
||||
if (!menu_input_dialog_start(&line))
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Send our nick */
|
||||
|
@ -1022,6 +1022,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
break;
|
||||
|
||||
case NETPLAY_CMD_LOAD_SAVESTATE:
|
||||
case NETPLAY_CMD_RESET:
|
||||
{
|
||||
uint32_t frame;
|
||||
uint32_t isize;
|
||||
@ -1071,7 +1072,11 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
* (strangely) force a rewind to the frame we're already on, so it
|
||||
* gets loaded. This is just to avoid having reloading implemented in
|
||||
* too many places. */
|
||||
if (cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))
|
||||
|
||||
/* Check the payload size */
|
||||
if ((cmd == NETPLAY_CMD_LOAD_SAVESTATE &&
|
||||
(cmd_size < 2*sizeof(uint32_t) || cmd_size > netplay->zbuffer_size + 2*sizeof(uint32_t))) ||
|
||||
(cmd == NETPLAY_CMD_RESET && cmd_size != sizeof(uint32_t)))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected payload size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
@ -1097,44 +1102,58 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
goto shrt;
|
||||
}
|
||||
|
||||
RECV(&isize, sizeof(isize))
|
||||
/* Now we switch based on whether we're loading a state or resetting */
|
||||
if (cmd == NETPLAY_CMD_LOAD_SAVESTATE)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
isize = ntohl(isize);
|
||||
RECV(&isize, sizeof(isize))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive inflated size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
isize = ntohl(isize);
|
||||
|
||||
if (isize != netplay->state_size)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
if (isize != netplay->state_size)
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE received an unexpected save state size.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
RECV(netplay->zbuffer, cmd_size - 2*sizeof(uint32_t))
|
||||
{
|
||||
RARCH_ERR("CMD_LOAD_SAVESTATE failed to receive savestate.\n");
|
||||
return netplay_cmd_nak(netplay, connection);
|
||||
}
|
||||
|
||||
/* And decompress it */
|
||||
switch (connection->compression_supported)
|
||||
{
|
||||
case NETPLAY_COMPRESSION_ZLIB:
|
||||
ctrans = &netplay->compress_zlib;
|
||||
break;
|
||||
default:
|
||||
ctrans = &netplay->compress_nil;
|
||||
}
|
||||
ctrans->decompression_backend->set_in(ctrans->decompression_stream,
|
||||
netplay->zbuffer, cmd_size - 2*sizeof(uint32_t));
|
||||
ctrans->decompression_backend->set_out(ctrans->decompression_stream,
|
||||
(uint8_t*)netplay->buffer[netplay->read_ptr[connection->player]].state,
|
||||
netplay->state_size);
|
||||
ctrans->decompression_backend->trans(ctrans->decompression_stream,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
/* Force a rewind to the relevant frame */
|
||||
netplay->force_rewind = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Resetting */
|
||||
netplay->force_reset = true;
|
||||
|
||||
/* And decompress it */
|
||||
switch (connection->compression_supported)
|
||||
{
|
||||
case NETPLAY_COMPRESSION_ZLIB:
|
||||
ctrans = &netplay->compress_zlib;
|
||||
break;
|
||||
default:
|
||||
ctrans = &netplay->compress_nil;
|
||||
}
|
||||
ctrans->decompression_backend->set_in(ctrans->decompression_stream,
|
||||
netplay->zbuffer, cmd_size - 2*sizeof(uint32_t));
|
||||
ctrans->decompression_backend->set_out(ctrans->decompression_stream,
|
||||
(uint8_t*)netplay->buffer[netplay->read_ptr[connection->player]].state,
|
||||
netplay->state_size);
|
||||
ctrans->decompression_backend->trans(ctrans->decompression_stream,
|
||||
true, &rd, &wn, NULL);
|
||||
|
||||
/* Skip ahead if it's past where we are */
|
||||
if (frame > netplay->run_frame_count)
|
||||
if (frame > netplay->run_frame_count ||
|
||||
cmd == NETPLAY_CMD_RESET)
|
||||
{
|
||||
/* This is squirrely: We need to assure that when we advance the
|
||||
* frame in post_frame, THEN we're referring to the frame to
|
||||
@ -1161,8 +1180,7 @@ static bool netplay_get_cmd(netplay_t *netplay,
|
||||
}
|
||||
}
|
||||
|
||||
/* And force rewind to it */
|
||||
netplay->force_rewind = true;
|
||||
/* Make sure our states are correct */
|
||||
netplay->savestate_request_outstanding = false;
|
||||
netplay->other_ptr = netplay->read_ptr[connection->player];
|
||||
netplay->other_frame_count = frame;
|
||||
|
@ -153,8 +153,11 @@ enum netplay_cmd
|
||||
/* Request that a client stall because it's running fast */
|
||||
NETPLAY_CMD_STALL = 0x0045,
|
||||
|
||||
/* Request a core reset */
|
||||
NETPLAY_CMD_RESET = 0x0046,
|
||||
|
||||
/* Sends over cheats enabled on client (unsupported) */
|
||||
NETPLAY_CMD_CHEATS = 0x0046,
|
||||
NETPLAY_CMD_CHEATS = 0x0047,
|
||||
|
||||
/* Misc. commands */
|
||||
|
||||
@ -404,6 +407,9 @@ struct netplay
|
||||
* events, such as player flipping or savestate loading. */
|
||||
bool force_rewind;
|
||||
|
||||
/* Force a reset */
|
||||
bool force_reset;
|
||||
|
||||
/* Quirks in the savestate implementation */
|
||||
uint64_t quirks;
|
||||
|
||||
|
@ -396,6 +396,13 @@ void netplay_sync_post_frame(netplay_t *netplay, bool stalled)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reset if it was requested */
|
||||
if (netplay->force_reset)
|
||||
{
|
||||
core_reset();
|
||||
netplay->force_reset = false;
|
||||
}
|
||||
|
||||
#ifndef DEBUG_NONDETERMINISTIC_CORES
|
||||
if (!netplay->force_rewind)
|
||||
{
|
||||
|
@ -3,9 +3,16 @@ package com.retroarch.browser.retroactivity;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.content.Intent;
|
||||
import android.content.Context;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import android.hardware.input.InputManager;
|
||||
|
||||
public final class RetroActivityFuture extends RetroActivityCamera {
|
||||
|
||||
// If set to true then Retroarch will completely exit when it loses focus
|
||||
private boolean quitfocus = false;
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
@ -28,7 +35,7 @@ public final class RetroActivityFuture extends RetroActivityCamera {
|
||||
| API_SYSTEM_UI_FLAG_FULLSCREEN
|
||||
| API_SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
|
||||
|
||||
// Check for REFRESH parameter
|
||||
// Check for Android UI specific parameters
|
||||
Intent retro = getIntent();
|
||||
String refresh = retro.getStringExtra("REFRESH");
|
||||
|
||||
@ -38,7 +45,40 @@ public final class RetroActivityFuture extends RetroActivityCamera {
|
||||
params.preferredRefreshRate = Integer.parseInt(refresh);
|
||||
getWindow().setAttributes(params);
|
||||
}
|
||||
|
||||
// If QUITFOCUS parameter is provided then enable that Retroarch quits when focus is lost
|
||||
quitfocus = retro.hasExtra("QUITFOCUS");
|
||||
|
||||
// If HIDEMOUSE parameters is provided then hide the mourse cursor
|
||||
// This requires NVIDIA Android extensions (available on NVIDIA Shield), if they are not
|
||||
// available then nothing will be done
|
||||
if (retro.hasExtra("HIDEMOUSE")) hideMouseCursor();
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMouseCursor() {
|
||||
|
||||
// Check for NVIDIA extensions and minimum SDK version
|
||||
Method mInputManager_setCursorVisibility;
|
||||
try { mInputManager_setCursorVisibility =
|
||||
InputManager.class.getMethod("setCursorVisibility", boolean.class);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
return; // Extensions were not available so do nothing
|
||||
}
|
||||
|
||||
// Hide the mouse cursor
|
||||
InputManager inputManager = (InputManager) getSystemService(Context.INPUT_SERVICE);
|
||||
try { mInputManager_setCursorVisibility.invoke(inputManager, false); }
|
||||
catch (InvocationTargetException ite) { }
|
||||
catch (IllegalAccessException iae) { }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
// If QUITFOCUS parameter was set then completely exit Retroarch when focus is lost
|
||||
if (quitfocus) System.exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -139,7 +139,7 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="msimg32.lib"
|
||||
AdditionalDependencies="msimg32.lib winmm.lib"
|
||||
OutputFile="$(OutDir)/RetroArch-msvc2005.exe"
|
||||
LinkIncremental="1"
|
||||
GenerateDebugInformation="true"
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user