mirror of
https://gitlab.com/niansa/llama_nds.git
synced 2025-03-06 20:53:28 +01:00
Initial commit
This commit is contained in:
commit
06f836c0d4
12 changed files with 779 additions and 0 deletions
74
.gitignore
vendored
Normal file
74
.gitignore
vendored
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
# This file is used to ignore files which are generated
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
*~
|
||||||
|
*.autosave
|
||||||
|
*.a
|
||||||
|
*.core
|
||||||
|
*.moc
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
*.orig
|
||||||
|
*.rej
|
||||||
|
*.so
|
||||||
|
*.so.*
|
||||||
|
*_pch.h.cpp
|
||||||
|
*_resource.rc
|
||||||
|
*.qm
|
||||||
|
.#*
|
||||||
|
*.*#
|
||||||
|
core
|
||||||
|
!core/
|
||||||
|
tags
|
||||||
|
.DS_Store
|
||||||
|
.directory
|
||||||
|
*.debug
|
||||||
|
Makefile*
|
||||||
|
*.prl
|
||||||
|
*.app
|
||||||
|
moc_*.cpp
|
||||||
|
ui_*.h
|
||||||
|
qrc_*.cpp
|
||||||
|
Thumbs.db
|
||||||
|
*.res
|
||||||
|
*.rc
|
||||||
|
/.qmake.cache
|
||||||
|
/.qmake.stash
|
||||||
|
|
||||||
|
# qtcreator generated files
|
||||||
|
*.pro.user*
|
||||||
|
CMakeLists.txt.user*
|
||||||
|
|
||||||
|
# xemacs temporary files
|
||||||
|
*.flc
|
||||||
|
|
||||||
|
# Vim temporary files
|
||||||
|
.*.swp
|
||||||
|
|
||||||
|
# Visual Studio generated files
|
||||||
|
*.ib_pdb_index
|
||||||
|
*.idb
|
||||||
|
*.ilk
|
||||||
|
*.pdb
|
||||||
|
*.sln
|
||||||
|
*.suo
|
||||||
|
*.vcproj
|
||||||
|
*vcproj.*.*.user
|
||||||
|
*.ncb
|
||||||
|
*.sdf
|
||||||
|
*.opensdf
|
||||||
|
*.vcxproj
|
||||||
|
*vcxproj.*
|
||||||
|
|
||||||
|
# MinGW generated files
|
||||||
|
*.Debug
|
||||||
|
*.Release
|
||||||
|
|
||||||
|
# Python byte code
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Binaries
|
||||||
|
# --------
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
|
30
CMakeLists.txt
Normal file
30
CMakeLists.txt
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
|
||||||
|
project(llama.any LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
add_executable(llama
|
||||||
|
main.cpp
|
||||||
|
Client.hpp Client.cpp
|
||||||
|
Socket.hpp
|
||||||
|
Receiver.hpp Receiver.cpp
|
||||||
|
Sender.hpp Sender.cpp
|
||||||
|
Runtime.cpp Runtime.hpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(llama PUBLIC PLATFORM="${CMAKE_SYSTEM_NAME}")
|
||||||
|
if (CMAKE_SYSTEM_NAME STREQUAL Nintendo3DS)
|
||||||
|
target_compile_definitions(llama PUBLIC PLATFORM_3DS)
|
||||||
|
elseif (CMAKE_SYSTEM_NAME STREQUAL NintendoDS)
|
||||||
|
target_compile_definitions(llama PUBLIC PLATFORM_DS)
|
||||||
|
target_link_libraries(llama PUBLIC dswifi9 nds9)
|
||||||
|
elseif (CMAKE_SYSTEM_NAME STREQUAL Linux)
|
||||||
|
target_compile_definitions(llama PUBLIC PLATFORM_LINUX)
|
||||||
|
elseif (CMAKE_SYSTEM_NAME STREQUAL Windows)
|
||||||
|
target_compile_definitions(llama PUBLIC PLATFORM_WINDOWS)
|
||||||
|
target_link_libraries(llama PUBLIC Ws2_32)
|
||||||
|
else()
|
||||||
|
message(SEND_ERROR "${CMAKE_SYSTEM_NAME} is not a supported platform!")
|
||||||
|
endif()
|
113
Client.cpp
Normal file
113
Client.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include "Client.hpp"
|
||||||
|
#include "Socket.hpp"
|
||||||
|
#include "Sender.hpp"
|
||||||
|
#include "Receiver.hpp"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#ifndef PLATFORM_WINDOWS
|
||||||
|
# include <sys/types.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <netinet/in.h>
|
||||||
|
# include <arpa/inet.h>
|
||||||
|
# include <netdb.h>
|
||||||
|
#else
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Client::fetchAddr(const std::string& addr, unsigned port) {
|
||||||
|
# ifdef HAS_ADDRINFO
|
||||||
|
// Set up hints
|
||||||
|
addrinfo hints;
|
||||||
|
memset(&hints, 0, sizeof(struct addrinfo));
|
||||||
|
hints.ai_family = AF_INET; //TODO: Care about IPv6
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
hints.ai_protocol = IPPROTO_TCP;
|
||||||
|
hints.ai_flags = AI_CANONNAME;
|
||||||
|
|
||||||
|
// Get port as C string
|
||||||
|
char portStr[7];
|
||||||
|
sprintf(portStr, "%u", port);
|
||||||
|
|
||||||
|
// Get addrinfo
|
||||||
|
auto error = getaddrinfo(addr.c_str(), portStr, &hints, &addrInfo);
|
||||||
|
auto bad = addrInfo == nullptr;
|
||||||
|
# else
|
||||||
|
addrInfo = gethostbyname(addr.c_str());
|
||||||
|
auto error = errno;
|
||||||
|
auto bad = addrInfo == nullptr || addrInfo->h_addr_list[0] == nullptr;
|
||||||
|
# endif
|
||||||
|
|
||||||
|
// Check for error
|
||||||
|
if (bad) {
|
||||||
|
throw Exception("DNS failed to look up hostname: "+std::string(strerror(error))+" ("+addr+')');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Client::Client(const std::string& addr, unsigned port) {
|
||||||
|
// Create socket
|
||||||
|
connection = std::make_unique<SocketConnection<Sender::Simple, Receiver::Simple>>(Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)); //TODO: Care about IPv6
|
||||||
|
if (*connection < 0) [[unlikely]] {
|
||||||
|
throw Exception("Failed to create TCP socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch address
|
||||||
|
fetchAddr(addr, port);
|
||||||
|
|
||||||
|
# ifdef HAS_ADDRINFO
|
||||||
|
// Connect to server
|
||||||
|
if (connect(*connection, addrInfo->ai_addr, addrInfo->ai_addrlen) != 0) [[unlikely]] {
|
||||||
|
throw Exception("Connection for HTTP::Request has been declined");
|
||||||
|
}
|
||||||
|
# else
|
||||||
|
// Connect to server
|
||||||
|
struct sockaddr_in sain;
|
||||||
|
sain.sin_family = AF_INET;
|
||||||
|
sain.sin_port = htons(port);
|
||||||
|
sain.sin_addr.s_addr = *reinterpret_cast<unsigned long *>(addrInfo->h_addr_list[0]);
|
||||||
|
if (connect(*connection, reinterpret_cast<sockaddr *>(&sain), sizeof(sain)) != 0) [[unlikely]] {
|
||||||
|
throw Exception("Connection has been declined");
|
||||||
|
}
|
||||||
|
# endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::ask(std::string_view prompt, const std::function<void (unsigned progress)>& on_progress, const std::function<void (std::string_view token)>& on_token) {
|
||||||
|
std::string fres;
|
||||||
|
|
||||||
|
// Send prompt length
|
||||||
|
uint8_t len = prompt.length();
|
||||||
|
connection->writeObject(len, true);
|
||||||
|
|
||||||
|
// Send prompt
|
||||||
|
connection->write(prompt);
|
||||||
|
|
||||||
|
// Receive progress
|
||||||
|
for (;;) {
|
||||||
|
uint8_t progress;
|
||||||
|
|
||||||
|
// Receive percentage
|
||||||
|
connection->readObject(progress);
|
||||||
|
|
||||||
|
// Run on_progress callback
|
||||||
|
on_progress(progress);
|
||||||
|
|
||||||
|
// Stop at 100%
|
||||||
|
if (progress == 100) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receive response
|
||||||
|
for (;;) {
|
||||||
|
// Receive response length
|
||||||
|
connection->readObject(len);
|
||||||
|
|
||||||
|
// End if zero
|
||||||
|
if (len == 0xFF) break;
|
||||||
|
|
||||||
|
// Receive response
|
||||||
|
const auto token = connection->read(len);
|
||||||
|
|
||||||
|
// Run on_token callback
|
||||||
|
on_token(token);
|
||||||
|
}
|
||||||
|
}
|
45
Client.hpp
Normal file
45
Client.hpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#ifndef CLIENT_HPP
|
||||||
|
#define CLIENT_HPP
|
||||||
|
#include "Runtime.hpp"
|
||||||
|
#include "Socket.hpp"
|
||||||
|
#include "Sender.hpp"
|
||||||
|
#include "Receiver.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#ifndef PLATFORM_WINDOWS
|
||||||
|
# include <netdb.h>
|
||||||
|
#else
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class Client
|
||||||
|
{
|
||||||
|
struct Exception : public std::runtime_error {
|
||||||
|
using std::runtime_error::runtime_error;
|
||||||
|
};
|
||||||
|
|
||||||
|
int fd = -1;
|
||||||
|
# ifdef HAS_ADDRINFO
|
||||||
|
addrinfo
|
||||||
|
# else
|
||||||
|
bool sent = false;
|
||||||
|
hostent
|
||||||
|
# endif
|
||||||
|
*addrInfo; // Can't be null unless request has already been sent
|
||||||
|
|
||||||
|
std::unique_ptr<SocketConnection<Sender::Simple, Receiver::Simple>> connection;
|
||||||
|
|
||||||
|
void fetchAddr(const std::string& addr, unsigned port);
|
||||||
|
|
||||||
|
public:
|
||||||
|
Client(const std::string &addr, unsigned port);
|
||||||
|
|
||||||
|
void ask(std::string_view prompt, const std::function<void (unsigned progress)>& on_progress, const std::function<void (std::string_view token)>& on_token);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CLIENT_HPP
|
43
Receiver.cpp
Normal file
43
Receiver.cpp
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
#include "Receiver.hpp"
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <array>
|
||||||
|
#ifndef PLATFORM_WINDOWS
|
||||||
|
# include <sys/select.h>
|
||||||
|
# include <sys/socket.h>
|
||||||
|
#else
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
std::string Receiver::Simple::read(size_t amount) {
|
||||||
|
// Create buffer
|
||||||
|
std::string fres;
|
||||||
|
fres.resize(amount);
|
||||||
|
|
||||||
|
// Read into buffer
|
||||||
|
read(reinterpret_cast<std::byte*>(fres.data()), fres.size());
|
||||||
|
|
||||||
|
// Return final buffer
|
||||||
|
return fres;
|
||||||
|
}
|
||||||
|
void Receiver::Simple::read(std::byte *buffer, size_t size) {
|
||||||
|
recv(fd, buffer, size, MSG_WAITALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Receiver::Simple::readSome(size_t max) {
|
||||||
|
// Create buffer
|
||||||
|
std::string fres;
|
||||||
|
fres.resize(max);
|
||||||
|
|
||||||
|
// Receive data
|
||||||
|
ssize_t bytesRead;
|
||||||
|
if ((bytesRead = recv(fd, fres.data(), max, MSG_WAITALL)) < 0) [[unlikely]] {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resize and return final buffer
|
||||||
|
fres.resize(bytesRead);
|
||||||
|
return fres;
|
||||||
|
}
|
30
Receiver.hpp
Normal file
30
Receiver.hpp
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
#ifndef _RECEIVER_HPP
|
||||||
|
#define _RECEIVER_HPP
|
||||||
|
#include "Runtime.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Receiver {
|
||||||
|
class Simple {
|
||||||
|
protected:
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Simple(int fd) : fd(fd) {}
|
||||||
|
|
||||||
|
// Reads the exact amount of bytes given
|
||||||
|
std::string read(size_t amount);
|
||||||
|
void read(std::byte *buffer, size_t size);
|
||||||
|
// Reads at max. the amount of bytes given
|
||||||
|
std::string readSome(size_t max);
|
||||||
|
|
||||||
|
// Reads an object of type T
|
||||||
|
template<typename T>
|
||||||
|
auto readObject(T& o) {
|
||||||
|
return read(reinterpret_cast<std::byte *>(&o), sizeof(o));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
47
Runtime.cpp
Normal file
47
Runtime.cpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#include "Runtime.hpp"
|
||||||
|
|
||||||
|
#ifdef PLATFORM_3DS
|
||||||
|
#include <string>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <3ds.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Runtime::customTerminate() noexcept {
|
||||||
|
// Get error message
|
||||||
|
std::string message;
|
||||||
|
try {
|
||||||
|
std::rethrow_exception(std::current_exception());
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
message = e.what();
|
||||||
|
} catch (...) {
|
||||||
|
message = "Unknown";
|
||||||
|
}
|
||||||
|
// Display error
|
||||||
|
errorConf conf = {
|
||||||
|
.type = ERROR_TEXT_WORD_WRAP,
|
||||||
|
.errorCode = errno,
|
||||||
|
.upperScreenFlag = ERROR_NORMAL,
|
||||||
|
.useLanguage = CFG_LANGUAGE_EN,
|
||||||
|
.Text = {L'I', L'N', L'V', L'A', L'L', L'I', L'D', L'\0'},
|
||||||
|
.homeButton = true,
|
||||||
|
.softwareReset = false,
|
||||||
|
.appJump = false,
|
||||||
|
.returnCode = ERROR_UNKNOWN,
|
||||||
|
.eulaVersion = 0
|
||||||
|
};
|
||||||
|
errorText(&conf, ("An exception was thrown but never handled:\n\n"+message).c_str());
|
||||||
|
errorDisp(&conf);
|
||||||
|
// Exit
|
||||||
|
aptExit();
|
||||||
|
socExit();
|
||||||
|
gfxExit();
|
||||||
|
exit(-errno);
|
||||||
|
}
|
||||||
|
#elif PLATFORM_DS
|
||||||
|
void Runtime::kbCallback(int key) {
|
||||||
|
if (key > 0) printf("%c", key);
|
||||||
|
}
|
||||||
|
#endif
|
235
Runtime.hpp
Normal file
235
Runtime.hpp
Normal file
|
@ -0,0 +1,235 @@
|
||||||
|
#ifndef _RUNTIME_HPP
|
||||||
|
#define _RUNTIME_HPP
|
||||||
|
|
||||||
|
#ifdef PLATFORM_3DS
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <cstring>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <3ds.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Runtime {
|
||||||
|
u32 *SOC_buffer = NULL;
|
||||||
|
constexpr static auto SOC_ALIGN = 0x1000,
|
||||||
|
SOC_BUFFERSIZE = 0x100000;
|
||||||
|
|
||||||
|
[[noreturn]] static void customTerminate() noexcept;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Runtime() {
|
||||||
|
std::set_terminate(customTerminate);
|
||||||
|
gfxInitDefault();
|
||||||
|
consoleInit(GFX_TOP, NULL);
|
||||||
|
aptInit();
|
||||||
|
SOC_buffer = (u32*)memalign(SOC_ALIGN, SOC_BUFFERSIZE);
|
||||||
|
auto ret = socInit(SOC_buffer, SOC_BUFFERSIZE);
|
||||||
|
if (ret != 0) {
|
||||||
|
throw std::runtime_error("socInit() = "+std::to_string((unsigned int)ret));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Runtime(Runtime&) = delete;
|
||||||
|
Runtime(const Runtime&) = delete;
|
||||||
|
Runtime(Runtime&&) = delete;
|
||||||
|
~Runtime() {
|
||||||
|
aptSetHomeAllowed(false);
|
||||||
|
std::cout << std::flush;
|
||||||
|
std::cerr << std::flush;
|
||||||
|
std::clog << std::endl << "Runtime destroyed." << std::endl;
|
||||||
|
std::clog << "Press START to exit" << std::flush;
|
||||||
|
for (u32 kDown; !(hidKeysDown() & KEY_START) && cooperate(); hidScanInput()) {
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||||
|
}
|
||||||
|
aptExit();
|
||||||
|
socExit();
|
||||||
|
gfxExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
bool cooperate() noexcept {
|
||||||
|
return aptMainLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *readInput(const char *hint) {
|
||||||
|
static SwkbdState swkbd;
|
||||||
|
static char swkbd_buf[2048];
|
||||||
|
// Read input
|
||||||
|
memset(swkbd_buf, 0, sizeof(swkbd_buf));
|
||||||
|
swkbdInit(&swkbd, SWKBD_TYPE_NORMAL, 3, sizeof(swkbd_buf));
|
||||||
|
swkbdSetHintText(&swkbd, hint);
|
||||||
|
swkbdInputText(&swkbd, swkbd_buf, sizeof(swkbd_buf));
|
||||||
|
// Return input as string
|
||||||
|
return swkbd_buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearScreen() {
|
||||||
|
consoleClear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#elif PLATFORM_DS
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <thread>
|
||||||
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <nds.h>
|
||||||
|
#include <dswifi9.h>
|
||||||
|
|
||||||
|
|
||||||
|
class Runtime {
|
||||||
|
static void kbCallback(int key);
|
||||||
|
|
||||||
|
Keyboard *swkbd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Runtime() {
|
||||||
|
// Initialize console
|
||||||
|
consoleDemoInit();
|
||||||
|
|
||||||
|
// Initialize WiFi
|
||||||
|
std::cout << "Connecting via WFC data" << std::endl;
|
||||||
|
if (!Wifi_InitDefault(WFC_CONNECT)) {
|
||||||
|
throw std::runtime_error("Failed to enable WiFi");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize keyboard
|
||||||
|
swkbd = keyboardDemoInit();
|
||||||
|
swkbd->OnKeyPressed = kbCallback;
|
||||||
|
}
|
||||||
|
Runtime(Runtime&) = delete;
|
||||||
|
Runtime(const Runtime&) = delete;
|
||||||
|
Runtime(Runtime&&) = delete;
|
||||||
|
~Runtime() {}
|
||||||
|
|
||||||
|
static inline
|
||||||
|
bool cooperate() noexcept {
|
||||||
|
// The Nintendo DS does not support multitasking
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *readInput(const char *hint) {
|
||||||
|
std::cout << hint << ": " << std::flush;
|
||||||
|
static std::string outstr;
|
||||||
|
std::getline(std::cin, outstr);
|
||||||
|
return outstr.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearScreen() {
|
||||||
|
consoleClear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#elif PLATFORM_LINUX
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <mutex>
|
||||||
|
#include <cstring>
|
||||||
|
#include <csignal>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Runtime {
|
||||||
|
static inline bool stopping = false;
|
||||||
|
static inline void handler(int signo, siginfo_t *info, void *context) {
|
||||||
|
stopping = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Runtime() {
|
||||||
|
struct sigaction act = { 0 };
|
||||||
|
act.sa_flags = SA_SIGINFO;
|
||||||
|
act.sa_sigaction = handler;
|
||||||
|
for (int sig : {SIGTERM, SIGINT, SIGQUIT, SIGHUP}) {
|
||||||
|
if (sigaction(sig, &act, nullptr) < 0) {
|
||||||
|
throw std::runtime_error("sigaction() = "+std::string(strerror(errno)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Runtime(Runtime&) = delete;
|
||||||
|
Runtime(const Runtime&) = delete;
|
||||||
|
Runtime(Runtime&&) = delete;
|
||||||
|
~Runtime() {
|
||||||
|
std::cout << std::flush;
|
||||||
|
std::cerr << std::flush;
|
||||||
|
std::clog << std::endl << "Runtime destroyed." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool cooperate() noexcept {
|
||||||
|
// Linux runs threads preemptively, no need to actually cooperate
|
||||||
|
return !stopping;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *readInput(const char *hint) {
|
||||||
|
static std::string content;
|
||||||
|
std::cout << hint << ": ";
|
||||||
|
std::getline(std::cin, content);
|
||||||
|
return content.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clearScreen() {
|
||||||
|
std::cout << "\033[H\033[2J\033[3J";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#elif PLATFORM_WINDOWS
|
||||||
|
#include <iostream>
|
||||||
|
#include <mutex>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <winsock2.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Runtime {
|
||||||
|
public:
|
||||||
|
Runtime() {
|
||||||
|
WSADATA wsaData;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
throw std::runtime_error("Failed to initialize WinSock");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Runtime(Runtime&) = delete;
|
||||||
|
Runtime(const Runtime&) = delete;
|
||||||
|
Runtime(Runtime&&) = delete;
|
||||||
|
~Runtime() {
|
||||||
|
std::cout << std::flush;
|
||||||
|
std::cerr << std::flush;
|
||||||
|
std::clog << std::endl << "Runtime destroyed." << std::endl;
|
||||||
|
WSACleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr bool cooperate() noexcept {
|
||||||
|
// Windows runs threads preemptively, no need to cooperate.
|
||||||
|
// No signals to handle either, Windows doesn't support them.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *readInput(const char *hint) {
|
||||||
|
static std::string content;
|
||||||
|
std::cout << hint << ": ";
|
||||||
|
std::getline(std::cin, content);
|
||||||
|
return content.c_str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !(defined(PLATFORM_WINDOWS) || defined(PLATFORM_DS))
|
||||||
|
# define MSG_FLAGS_OR_ZERO(...) __VA_ARGS__
|
||||||
|
#else
|
||||||
|
# define MSG_FLAGS_OR_ZERO(...) 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef PLATFORM_DS
|
||||||
|
# define IPPROTO_TCP 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef PLATFORM_DS
|
||||||
|
# define HAS_ADDRINFO
|
||||||
|
#endif
|
24
Sender.cpp
Normal file
24
Sender.cpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#include "Runtime.hpp"
|
||||||
|
#include "Sender.hpp"
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#ifndef PLATFORM_WINDOWS
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
#else
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
# include <winsock2.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Sender::Simple::write(std::string_view str, bool moreData) {
|
||||||
|
this->write(reinterpret_cast<const std::byte*>(str.data()), str.size(), moreData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sender::Simple::write(const std::byte *data, size_t size, bool moreData) {
|
||||||
|
std::string fres;
|
||||||
|
|
||||||
|
// Write
|
||||||
|
send(fd, reinterpret_cast<const char*>(data), size, MSG_FLAGS_OR_ZERO(MSG_WAITALL | MSG_NOSIGNAL | (int(moreData)*MSG_MORE)));
|
||||||
|
}
|
25
Sender.hpp
Normal file
25
Sender.hpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#ifndef _SENDER_HPP
|
||||||
|
#define _SENDER_HPP
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
|
||||||
|
namespace Sender {
|
||||||
|
class Simple {
|
||||||
|
protected:
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Simple(int fd) : fd(fd) {}
|
||||||
|
|
||||||
|
void write(std::string_view, bool moreData = false);
|
||||||
|
void write(const std::byte *data, size_t, bool moreData = false);
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
auto writeObject(const T& o, bool moreData = false) {
|
||||||
|
return write(reinterpret_cast<const std::byte *>(&o), sizeof(o), moreData);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
68
Socket.hpp
Normal file
68
Socket.hpp
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
#ifndef _SOCKET_HPP
|
||||||
|
#define _SOCKET_HPP
|
||||||
|
#include <memory>
|
||||||
|
#include <unistd.h>
|
||||||
|
#if defined(PLATFORM_WINDOWS)
|
||||||
|
# include <ws2tcpip.h>
|
||||||
|
#elif defined(PLATFORM_WII)
|
||||||
|
#include <network.h>
|
||||||
|
#else
|
||||||
|
# include <sys/socket.h>
|
||||||
|
# include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class Socket {
|
||||||
|
int fd;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void reset() {
|
||||||
|
fd = -1;
|
||||||
|
}
|
||||||
|
void set(int _fd) {
|
||||||
|
fd = _fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using Port = uint16_t;
|
||||||
|
|
||||||
|
Socket() : fd(-1) {}
|
||||||
|
Socket(int domain, int type, int protocol) {
|
||||||
|
fd = socket(domain, type, protocol);
|
||||||
|
}
|
||||||
|
Socket(Socket&) = delete;
|
||||||
|
Socket(const Socket&) = delete;
|
||||||
|
Socket(Socket&& o) : fd(o.fd) {
|
||||||
|
o.fd = -1;
|
||||||
|
}
|
||||||
|
auto& operator =(Socket&& o) {
|
||||||
|
close(fd);
|
||||||
|
fd = o.fd;
|
||||||
|
o.fd = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
~Socket() {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
operator int() const {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get() const {
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template<class SenderT, class ReceiverT>
|
||||||
|
class SocketConnection : public SenderT, public ReceiverT, public Socket {
|
||||||
|
public:
|
||||||
|
SocketConnection(Socket&& socket)
|
||||||
|
// Double-initialization seems to yield better assembly
|
||||||
|
: SenderT(socket), ReceiverT(socket), Socket(std::move(socket)) {
|
||||||
|
SenderT::fd = get();
|
||||||
|
ReceiverT::fd = get();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
45
main.cpp
Normal file
45
main.cpp
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
#include "Runtime.hpp"
|
||||||
|
#include "Client.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void on_progress(float progress) {
|
||||||
|
std::cout << unsigned(progress) << '\r' << std::flush;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Runtime rt;
|
||||||
|
|
||||||
|
// Print header
|
||||||
|
std::cout << "llama.any running on " PLATFORM ".\n"
|
||||||
|
"\n";
|
||||||
|
|
||||||
|
// Ask for server address
|
||||||
|
const std::string addr = rt.readInput("Server address");
|
||||||
|
|
||||||
|
// Create client
|
||||||
|
Client client(addr, 99181);
|
||||||
|
|
||||||
|
// Connection loop
|
||||||
|
for (;; rt.cooperate()) {
|
||||||
|
// Clear screen
|
||||||
|
rt.clearScreen();
|
||||||
|
|
||||||
|
// Run inference
|
||||||
|
client.ask(rt.readInput("Prompt"), [&rt] (float progress) {
|
||||||
|
std::cout << unsigned(progress) << "%\r" << std::flush;
|
||||||
|
rt.cooperate();
|
||||||
|
}, [&rt] (std::string_view token) {
|
||||||
|
std::cout << token << std::flush;
|
||||||
|
rt.cooperate();
|
||||||
|
});
|
||||||
|
std::cout << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue