mirror of
https://github.com/libretro/RetroArch
synced 2025-01-31 15:32:59 +00:00
23f0a85446
== DETAILS The broadcast address is a standard part of TCP/IP that is used to send messages to everyone on the subnet. This patch updates the logging code to do the following: 1. Derive the broadcast address from the Wii U's own IP address and subnet mask. These can all be obtained at runtime, which means we can... 2. Remove the PC_DEVELOPMENT_IP_ADDRESS define from Wii U's Makefile, because compiling in an IP is no longer needed. 3. Rewrite the net_listen script to listen for broadcast packets and print them out with timestamps. Since it's using the broadcast address, the only requirement is that the PC be on the same network subnet as the Wii U. Because of the low overhead of UDP, I've made logging on by default. This will make it a ton easier to get useful bug reports from users.
621 lines
16 KiB
C
621 lines
16 KiB
C
/* RetroArch - A frontend for libretro.
|
|
* Copyright (C) 2014-2016 - Ali Bouhlel
|
|
* Copyright (C) 2011-2017 - Daniel De Matteis
|
|
*
|
|
* 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 <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <sys/iosupport.h>
|
|
#include <net/net_compat.h>
|
|
#include <sys/socket.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <wiiu/types.h>
|
|
#include <wiiu/ac.h>
|
|
#include <file/file_path.h>
|
|
|
|
#ifndef IS_SALAMANDER
|
|
#include <lists/file_list.h>
|
|
#endif
|
|
|
|
#include <string/stdstring.h>
|
|
|
|
#include <wiiu/gx2.h>
|
|
#include <wiiu/kpad.h>
|
|
#include <wiiu/ios.h>
|
|
#include <wiiu/os.h>
|
|
#include <wiiu/procui.h>
|
|
#include <wiiu/sysapp.h>
|
|
|
|
#include "file_path_special.h"
|
|
|
|
#include "../frontend.h"
|
|
#include "../frontend_driver.h"
|
|
#include "../../defaults.h"
|
|
#include "../../paths.h"
|
|
#include "../../verbosity.h"
|
|
#include "../../retroarch.h"
|
|
#include "../../gfx/video_driver.h"
|
|
|
|
|
|
#include "hbl.h"
|
|
#include "wiiu_dbg.h"
|
|
#include "system/exception_handler.h"
|
|
#include "tasks/tasks_internal.h"
|
|
|
|
#ifndef IS_SALAMANDER
|
|
#ifdef HAVE_MENU
|
|
#include "../../menu/menu_driver.h"
|
|
#endif
|
|
#endif
|
|
|
|
#define WIIU_SD_PATH "sd:/"
|
|
#define WIIU_USB_PATH "usb:/"
|
|
|
|
/**
|
|
* The Wii U frontend driver, along with the main() method.
|
|
*/
|
|
|
|
static enum frontend_fork wiiu_fork_mode = FRONTEND_FORK_NONE;
|
|
static const char *elf_path_cst = WIIU_SD_PATH "retroarch/retroarch.elf";
|
|
|
|
static void frontend_wiiu_get_environment_settings(int *argc, char *argv[],
|
|
void *args, void *params_data)
|
|
{
|
|
unsigned i;
|
|
(void)args;
|
|
|
|
fill_pathname_basedir(g_defaults.dirs[DEFAULT_DIR_PORT], elf_path_cst, sizeof(g_defaults.dirs[DEFAULT_DIR_PORT]));
|
|
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"downloads", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_ASSETS]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_ASSETS], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"media", sizeof(g_defaults.dirs[DEFAULT_DIR_ASSETS]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"cores", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CORE_INFO], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"info", sizeof(g_defaults.dirs[DEFAULT_DIR_CORE_INFO]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SAVESTATE], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"savestates", sizeof(g_defaults.dirs[DEFAULT_DIR_SAVESTATE]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SRAM], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"savefiles", sizeof(g_defaults.dirs[DEFAULT_DIR_SRAM]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_SYSTEM], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"system", sizeof(g_defaults.dirs[DEFAULT_DIR_SYSTEM]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_PLAYLIST], g_defaults.dirs[DEFAULT_DIR_CORE],
|
|
"playlists", sizeof(g_defaults.dirs[DEFAULT_DIR_PLAYLIST]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"config", sizeof(g_defaults.dirs[DEFAULT_DIR_MENU_CONFIG]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_REMAP], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"config/remaps", sizeof(g_defaults.dirs[DEFAULT_DIR_REMAP]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"filters", sizeof(g_defaults.dirs[DEFAULT_DIR_VIDEO_FILTER]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_DATABASE], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"database/rdb", sizeof(g_defaults.dirs[DEFAULT_DIR_DATABASE]));
|
|
fill_pathname_join(g_defaults.dirs[DEFAULT_DIR_CURSOR], g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
"database/cursors", sizeof(g_defaults.dirs[DEFAULT_DIR_CURSOR]));
|
|
fill_pathname_join(g_defaults.path.config, g_defaults.dirs[DEFAULT_DIR_PORT],
|
|
file_path_str(FILE_PATH_MAIN_CONFIG), sizeof(g_defaults.path.config));
|
|
|
|
for (i = 0; i < DEFAULT_DIR_LAST; i++)
|
|
{
|
|
const char *dir_path = g_defaults.dirs[i];
|
|
if (!string_is_empty(dir_path))
|
|
path_mkdir(dir_path);
|
|
}
|
|
}
|
|
|
|
static void frontend_wiiu_deinit(void *data)
|
|
{
|
|
(void)data;
|
|
}
|
|
|
|
static void frontend_wiiu_shutdown(bool unused)
|
|
{
|
|
(void)unused;
|
|
}
|
|
|
|
static void frontend_wiiu_init(void *data)
|
|
{
|
|
(void)data;
|
|
DEBUG_LINE();
|
|
verbosity_enable();
|
|
DEBUG_LINE();
|
|
}
|
|
|
|
|
|
static int frontend_wiiu_get_rating(void)
|
|
{
|
|
return 10;
|
|
}
|
|
|
|
enum frontend_architecture frontend_wiiu_get_architecture(void)
|
|
{
|
|
return FRONTEND_ARCH_PPC;
|
|
}
|
|
|
|
static int frontend_wiiu_parse_drive_list(void *data, bool load_content)
|
|
{
|
|
#ifndef IS_SALAMANDER
|
|
file_list_t *list = (file_list_t *)data;
|
|
enum msg_hash_enums enum_idx = load_content ?
|
|
MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR :
|
|
MSG_UNKNOWN;
|
|
|
|
if (!list)
|
|
return -1;
|
|
|
|
menu_entries_append_enum(list, WIIU_SD_PATH,
|
|
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
|
|
enum_idx,
|
|
FILE_TYPE_DIRECTORY, 0, 0);
|
|
|
|
menu_entries_append_enum(list, WIIU_USB_PATH,
|
|
msg_hash_to_str(MENU_ENUM_LABEL_FILE_DETECT_CORE_LIST_PUSH_DIR),
|
|
enum_idx,
|
|
FILE_TYPE_DIRECTORY, 0, 0);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void frontend_wiiu_exec(const char *path, bool should_load_game)
|
|
{
|
|
|
|
struct
|
|
{
|
|
u32 magic;
|
|
u32 argc;
|
|
char * argv[3];
|
|
char args[];
|
|
}*param = getApplicationEndAddr();
|
|
int len = 0;
|
|
param->argc = 0;
|
|
|
|
if(!path || !*path)
|
|
{
|
|
RARCH_LOG("No executable path provided, cannot Restart\n");
|
|
}
|
|
|
|
DEBUG_STR(path);
|
|
|
|
strcpy(param->args + len, elf_path_cst);
|
|
param->argv[param->argc] = param->args + len;
|
|
len += strlen(param->args + len) + 1;
|
|
param->argc++;
|
|
|
|
RARCH_LOG("Attempt to load core: [%s].\n", path);
|
|
#ifndef IS_SALAMANDER
|
|
if (should_load_game && !path_is_empty(RARCH_PATH_CONTENT))
|
|
{
|
|
strcpy(param->args + len, path_get(RARCH_PATH_CONTENT));
|
|
param->argv[param->argc] = param->args + len;
|
|
len += strlen(param->args + len) + 1;
|
|
param->argc++;
|
|
|
|
RARCH_LOG("content path: [%s].\n", path_get(RARCH_PATH_CONTENT));
|
|
}
|
|
#endif
|
|
param->argv[param->argc] = NULL;
|
|
|
|
{
|
|
if (HBL_loadToMemory(path, (u32)param->args - (u32)param + len) < 0)
|
|
RARCH_LOG("Failed to load core\n");
|
|
else
|
|
{
|
|
param->magic = ARGV_MAGIC;
|
|
ARGV_PTR = param;
|
|
DEBUG_VAR(param->argc);
|
|
DEBUG_VAR(param->argv);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifndef IS_SALAMANDER
|
|
static bool frontend_wiiu_set_fork(enum frontend_fork fork_mode)
|
|
{
|
|
switch (fork_mode)
|
|
{
|
|
case FRONTEND_FORK_CORE:
|
|
RARCH_LOG("FRONTEND_FORK_CORE\n");
|
|
wiiu_fork_mode = fork_mode;
|
|
break;
|
|
case FRONTEND_FORK_CORE_WITH_ARGS:
|
|
RARCH_LOG("FRONTEND_FORK_CORE_WITH_ARGS\n");
|
|
wiiu_fork_mode = fork_mode;
|
|
break;
|
|
case FRONTEND_FORK_RESTART:
|
|
RARCH_LOG("FRONTEND_FORK_RESTART\n");
|
|
/* NOTE: We don't implement Salamander, so just turn
|
|
* this into FRONTEND_FORK_CORE. */
|
|
wiiu_fork_mode = FRONTEND_FORK_CORE;
|
|
break;
|
|
case FRONTEND_FORK_NONE:
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
#endif
|
|
|
|
static void frontend_wiiu_exitspawn(char *s, size_t len)
|
|
{
|
|
bool should_load_game = false;
|
|
#ifndef IS_SALAMANDER
|
|
if (wiiu_fork_mode == FRONTEND_FORK_NONE)
|
|
return;
|
|
|
|
switch (wiiu_fork_mode)
|
|
{
|
|
case FRONTEND_FORK_CORE_WITH_ARGS:
|
|
should_load_game = true;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
#endif
|
|
frontend_wiiu_exec(s, should_load_game);
|
|
}
|
|
|
|
|
|
frontend_ctx_driver_t frontend_ctx_wiiu =
|
|
{
|
|
frontend_wiiu_get_environment_settings,
|
|
frontend_wiiu_init,
|
|
frontend_wiiu_deinit,
|
|
frontend_wiiu_exitspawn,
|
|
NULL, /* process_args */
|
|
frontend_wiiu_exec,
|
|
#ifdef IS_SALAMANDER
|
|
NULL, /* set_fork */
|
|
#else
|
|
frontend_wiiu_set_fork,
|
|
#endif
|
|
frontend_wiiu_shutdown,
|
|
NULL, /* get_name */
|
|
NULL, /* get_os */
|
|
frontend_wiiu_get_rating,
|
|
NULL, /* load_content */
|
|
frontend_wiiu_get_architecture,
|
|
NULL, /* get_powerstate */
|
|
frontend_wiiu_parse_drive_list,
|
|
NULL, /* get_mem_total */
|
|
NULL, /* get_mem_free */
|
|
NULL, /* install_signal_handler */
|
|
NULL, /* get_signal_handler_state */
|
|
NULL, /* set_signal_handler_state */
|
|
NULL, /* destroy_signal_handler_state */
|
|
NULL, /* attach_console */
|
|
NULL, /* detach_console */
|
|
NULL, /* watch_path_for_changes */
|
|
NULL, /* check_for_path_changes */
|
|
"wiiu",
|
|
NULL, /* get_video_driver */
|
|
};
|
|
|
|
/* main() and its supporting functions */
|
|
|
|
static void main_setup(void);
|
|
static void get_arguments(int *argc, char ***argv);
|
|
static void do_rarch_main(int argc, char **argv);
|
|
static void main_loop(void);
|
|
static void main_teardown(void);
|
|
|
|
static void init_network(void);
|
|
static void deinit_network(void);
|
|
static void init_logging(void);
|
|
static void deinit_logging(void);
|
|
static void wiiu_log_init(int port);
|
|
static void wiiu_log_deinit(void);
|
|
static ssize_t wiiu_log_write(struct _reent *r, void *fd, const char *ptr, size_t len);
|
|
static void init_pad_libraries(void);
|
|
static void deinit_pad_libraries(void);
|
|
static void SaveCallback(void);
|
|
static bool swap_is_pending(void *start_time);
|
|
|
|
static struct sockaddr_in broadcast;
|
|
static int wiiu_log_socket = -1;
|
|
static volatile int wiiu_log_lock = 0;
|
|
|
|
#if !defined(PC_DEVELOPMENT_TCP_PORT)
|
|
#define PC_DEVELOPMENT_TCP_PORT 4405
|
|
#endif
|
|
|
|
static devoptab_t dotab_stdout =
|
|
{
|
|
"stdout_net", /* device name */
|
|
0, /* size of file structure */
|
|
NULL, /* device open */
|
|
NULL, /* device close */
|
|
wiiu_log_write, /* device write */
|
|
NULL, /* ... */
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
main_setup();
|
|
get_arguments(&argc, &argv);
|
|
|
|
#ifdef IS_SALAMANDER
|
|
int salamander_main(int argc, char **argv);
|
|
salamander_main(argc, argv);
|
|
#else
|
|
do_rarch_main(argc, argv);
|
|
main_loop();
|
|
main_exit(NULL);
|
|
#endif /* IS_SALAMANDER */
|
|
main_teardown();
|
|
|
|
/* We always return 0 because if we don't, it can prevent loading a
|
|
* different RPX/ELF in HBL. */
|
|
return 0;
|
|
}
|
|
|
|
static void get_arguments(int *argc, char ***argv)
|
|
{
|
|
DEBUG_VAR(ARGV_PTR);
|
|
if(ARGV_PTR && ((u32)ARGV_PTR < 0x01000000))
|
|
{
|
|
struct
|
|
{
|
|
u32 magic;
|
|
u32 argc;
|
|
char *argv[3];
|
|
} *param = ARGV_PTR;
|
|
if(param->magic == ARGV_MAGIC)
|
|
{
|
|
*argc = param->argc;
|
|
*argv = param->argv;
|
|
}
|
|
ARGV_PTR = NULL;
|
|
}
|
|
|
|
DEBUG_VAR(argc);
|
|
DEBUG_VAR(argv[0]);
|
|
DEBUG_VAR(argv[1]);
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void main_setup(void)
|
|
{
|
|
setup_os_exceptions();
|
|
ProcUIInit(&SaveCallback);
|
|
init_network();
|
|
init_logging();
|
|
init_pad_libraries();
|
|
verbosity_enable();
|
|
fflush(stdout);
|
|
}
|
|
|
|
static void main_teardown(void)
|
|
{
|
|
deinit_pad_libraries();
|
|
ProcUIShutdown();
|
|
deinit_logging();
|
|
deinit_network();
|
|
}
|
|
|
|
static void main_loop(void)
|
|
{
|
|
unsigned sleep_ms = 0;
|
|
OSTime start_time;
|
|
int status;
|
|
|
|
do
|
|
{
|
|
if(video_driver_get_ptr(false))
|
|
{
|
|
start_time = OSGetSystemTime();
|
|
task_queue_wait(swap_is_pending, &start_time);
|
|
}
|
|
else
|
|
task_queue_wait(NULL, NULL);
|
|
|
|
status = runloop_iterate(&sleep_ms);
|
|
|
|
if(status == 1 && sleep_ms > 0)
|
|
usleep(sleep_ms);
|
|
|
|
if(status == -1)
|
|
break;
|
|
} while(true);
|
|
}
|
|
|
|
static void do_rarch_main(int argc, char **argv)
|
|
{
|
|
#if 0
|
|
int argc_ = 2;
|
|
char *argv_[] = { WIIU_SD_PATH "retroarch/retroarch.elf",
|
|
WIIU_SD_PATH "rom.sfc",
|
|
NULL };
|
|
rarch_main(argc_, argv_, NULL);
|
|
#else
|
|
rarch_main(argc, argv, NULL);
|
|
#endif /* if 0 */
|
|
}
|
|
|
|
static void SaveCallback(void)
|
|
{
|
|
OSSavesDone_ReadyToRelease();
|
|
}
|
|
|
|
static bool swap_is_pending(void *start_time)
|
|
{
|
|
uint32_t swap_count, flip_count;
|
|
OSTime last_flip, last_vsync;
|
|
|
|
GX2GetSwapStatus(&swap_count, &flip_count, &last_flip, &last_vsync);
|
|
return last_vsync < *(OSTime *)start_time;
|
|
}
|
|
|
|
static void init_network(void)
|
|
{
|
|
ACInitialize();
|
|
ACConnect();
|
|
#ifdef IS_SALAMANDER
|
|
socket_lib_init();
|
|
#else
|
|
network_init();
|
|
#endif /* IS_SALAMANDER */
|
|
}
|
|
|
|
static void deinit_network(void)
|
|
{
|
|
ACClose();
|
|
ACFinalize();
|
|
}
|
|
|
|
int getBroadcastAddress(ACIpAddress *broadcast)
|
|
{
|
|
ACIpAddress myIp, mySubnet;
|
|
ACResult result;
|
|
|
|
if(broadcast == NULL)
|
|
return -1;
|
|
|
|
result = ACGetAssignedAddress(&myIp);
|
|
if(result < 0)
|
|
return -1;
|
|
result = ACGetAssignedSubnet(&mySubnet);
|
|
if(result < 0)
|
|
return -1;
|
|
|
|
*broadcast = myIp | (~mySubnet);
|
|
return 0;
|
|
}
|
|
|
|
static void init_logging(void)
|
|
{
|
|
wiiu_log_init(PC_DEVELOPMENT_TCP_PORT);
|
|
devoptab_list[STD_OUT] = &dotab_stdout;
|
|
devoptab_list[STD_ERR] = &dotab_stdout;
|
|
}
|
|
|
|
static void deinit_logging(void)
|
|
{
|
|
fflush(stdout);
|
|
fflush(stderr);
|
|
|
|
wiiu_log_deinit();
|
|
}
|
|
|
|
static int broadcast_init(int port)
|
|
{
|
|
ACIpAddress broadcast_ip;
|
|
if(getBroadcastAddress(&broadcast_ip) < 0)
|
|
return -1;
|
|
|
|
memset(&broadcast, 0, sizeof(broadcast));
|
|
broadcast.sin_family = AF_INET;
|
|
broadcast.sin_port = htons(port);
|
|
broadcast.sin_addr.s_addr = htonl(broadcast_ip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void wiiu_log_init(int port)
|
|
{
|
|
wiiu_log_lock = 0;
|
|
|
|
if(broadcast_init(port) < 0)
|
|
return;
|
|
|
|
wiiu_log_socket = socket(AF_INET, SOCK_DGRAM, 0);
|
|
|
|
if(wiiu_log_socket < 0)
|
|
return;
|
|
|
|
struct sockaddr_in connect_addr;
|
|
memset(&connect_addr, 0, sizeof(connect_addr));
|
|
connect_addr.sin_family = AF_INET;
|
|
connect_addr.sin_port = 0;
|
|
connect_addr.sin_addr.s_addr = htonl(INADDR_ANY);
|
|
|
|
if( bind(wiiu_log_socket, (struct sockaddr *)&connect_addr, sizeof(connect_addr)) < 0)
|
|
{
|
|
socketclose(wiiu_log_socket);
|
|
wiiu_log_socket = -1;
|
|
return;
|
|
}
|
|
}
|
|
|
|
static void wiiu_log_deinit(void)
|
|
{
|
|
if(wiiu_log_socket >= 0)
|
|
{
|
|
socketclose(wiiu_log_socket);
|
|
wiiu_log_socket = -1;
|
|
}
|
|
}
|
|
|
|
static void init_pad_libraries(void)
|
|
{
|
|
#ifndef IS_SALAMANDER
|
|
KPADInit();
|
|
WPADEnableURCC(true);
|
|
WPADEnableWiiRemote(true);
|
|
#endif /* IS_SALAMANDER */
|
|
}
|
|
|
|
static void deinit_pad_libraries(void)
|
|
{
|
|
#ifndef IS_SALAMANDER
|
|
KPADShutdown();
|
|
#endif /* IS_SALAMANDER */
|
|
}
|
|
|
|
/* logging routines */
|
|
|
|
void net_print(const char *str)
|
|
{
|
|
wiiu_log_write(NULL, 0, str, strlen(str));
|
|
}
|
|
|
|
void net_print_exp(const char *str)
|
|
{
|
|
sendto(wiiu_log_socket, str, strlen(str), 0, (struct sockaddr *)&broadcast, sizeof(broadcast));
|
|
}
|
|
|
|
static ssize_t wiiu_log_write(struct _reent *r, void *fd, const char *ptr, size_t len)
|
|
{
|
|
if( wiiu_log_socket < 0)
|
|
return len;
|
|
|
|
while(wiiu_log_lock)
|
|
OSSleepTicks(((248625000 / 4)) / 1000);
|
|
|
|
wiiu_log_lock = 1;
|
|
|
|
int ret;
|
|
int remaining = len;
|
|
|
|
while(remaining > 0)
|
|
{
|
|
int block = remaining < 1472 ? remaining : 1472;
|
|
ret = sendto(wiiu_log_socket, ptr, block, 0, (struct sockaddr *)&broadcast, sizeof(broadcast));
|
|
|
|
if(ret < 0)
|
|
break;
|
|
|
|
remaining -= ret;
|
|
ptr += ret;
|
|
}
|
|
|
|
wiiu_log_lock = 0;
|
|
|
|
return len;
|
|
}
|