feat: port to zig
This commit is contained in:
parent
d4a6232234
commit
b529e2bba4
130
.clang-format
130
.clang-format
|
@ -1,130 +0,0 @@
|
||||||
---
|
|
||||||
AccessModifierOffset: 0
|
|
||||||
AlignAfterOpenBracket: BlockIndent
|
|
||||||
AlignArrayOfStructures: None
|
|
||||||
AlignConsecutiveAssignments: None
|
|
||||||
AlignConsecutiveMacros: None
|
|
||||||
AlignConsecutiveBitFields: None
|
|
||||||
AlignConsecutiveDeclarations: None
|
|
||||||
AlignEscapedNewlines: DontAlign
|
|
||||||
AlignOperands: DontAlign
|
|
||||||
AlignTrailingComments: false
|
|
||||||
AllowAllArgumentsOnNextLine: true
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
|
||||||
AllowShortBlocksOnASingleLine: Empty
|
|
||||||
AllowShortCaseLabelsOnASingleLine: false
|
|
||||||
AllowShortEnumsOnASingleLine: false
|
|
||||||
AllowShortFunctionsOnASingleLine: Empty
|
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
|
||||||
AllowShortLambdasOnASingleLine: All
|
|
||||||
AllowShortLoopsOnASingleLine: false
|
|
||||||
AlwaysBreakAfterReturnType: None
|
|
||||||
AlwaysBreakBeforeMultilineStrings: true
|
|
||||||
AlwaysBreakTemplateDeclarations: MultiLine
|
|
||||||
AttributeMacros: []
|
|
||||||
BinPackArguments: false
|
|
||||||
BinPackParameters: false
|
|
||||||
BitFieldColonSpacing: After
|
|
||||||
BraceWrapping:
|
|
||||||
AfterCaseLabel: false
|
|
||||||
AfterClass: false
|
|
||||||
AfterControlStatement: Never
|
|
||||||
AfterEnum: false
|
|
||||||
AfterFunction: false
|
|
||||||
AfterNamespace: false
|
|
||||||
AfterStruct: false
|
|
||||||
AfterUnion: false
|
|
||||||
AfterExternBlock: false
|
|
||||||
BeforeCatch: false
|
|
||||||
BeforeElse: false
|
|
||||||
BeforeLambdaBody: false
|
|
||||||
BeforeWhile: false
|
|
||||||
IndentBraces: false
|
|
||||||
SplitEmptyFunction: false
|
|
||||||
SplitEmptyRecord: false
|
|
||||||
SplitEmptyNamespace: false
|
|
||||||
BreakAfterJavaFieldAnnotations: true
|
|
||||||
#BreakArrays: false
|
|
||||||
BreakBeforeBinaryOperators: All
|
|
||||||
BreakBeforeBraces: Custom
|
|
||||||
BreakBeforeConceptDeclarations: true
|
|
||||||
BreakBeforeTernaryOperators: true
|
|
||||||
BreakConstructorInitializers: AfterColon
|
|
||||||
BreakInheritanceList: AfterColon
|
|
||||||
BreakStringLiterals: true
|
|
||||||
ColumnLimit: 90
|
|
||||||
CompactNamespaces: false
|
|
||||||
ConstructorInitializerIndentWidth: 4
|
|
||||||
ContinuationIndentWidth: 4
|
|
||||||
Cpp11BracedListStyle: false
|
|
||||||
DeriveLineEnding: false
|
|
||||||
DerivePointerAlignment: false
|
|
||||||
DisableFormat: false # wtf
|
|
||||||
EmptyLineAfterAccessModifier: Never
|
|
||||||
EmptyLineBeforeAccessModifier: Always
|
|
||||||
ExperimentalAutoDetectBinPacking: false
|
|
||||||
FixNamespaceComments: false
|
|
||||||
ForEachMacros: ["BOOST_FOREACH"]
|
|
||||||
IfMacros: []
|
|
||||||
IncludeBlocks: Regroup
|
|
||||||
IndentAccessModifiers: false
|
|
||||||
IndentCaseBlocks: false
|
|
||||||
IndentCaseLabels: true
|
|
||||||
IndentExternBlock: Indent
|
|
||||||
IndentGotoLabels: true
|
|
||||||
IndentPPDirectives: BeforeHash
|
|
||||||
#IndentRequiresClause: false
|
|
||||||
IndentWidth: 4
|
|
||||||
IndentWrappedFunctionNames: false
|
|
||||||
#InsertBraces: false
|
|
||||||
InsertTrailingCommas: Wrapped
|
|
||||||
JavaImportGroups: ["java"]
|
|
||||||
JavaScriptQuotes: Double
|
|
||||||
JavaScriptWrapImports: true
|
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
|
||||||
LambdaBodyIndentation: OuterScope
|
|
||||||
MaxEmptyLinesToKeep: 1
|
|
||||||
NamespaceIndentation: All
|
|
||||||
PackConstructorInitializers: NextLine
|
|
||||||
PointerAlignment: Left
|
|
||||||
QualifierAlignment: Left
|
|
||||||
ReferenceAlignment: Left
|
|
||||||
ReflowComments: true
|
|
||||||
#RemoveSemicolon: true
|
|
||||||
#RequiresClausePosition: OwnLine
|
|
||||||
#RequiresExpressionIndentation: OuterScope
|
|
||||||
SeparateDefinitionBlocks: Always
|
|
||||||
SortIncludes: CaseInsensitive
|
|
||||||
SortJavaStaticImport: Before
|
|
||||||
SortUsingDeclarations: true
|
|
||||||
SpaceAfterCStyleCast: true
|
|
||||||
SpaceAfterLogicalNot: false
|
|
||||||
SpaceAfterTemplateKeyword: false
|
|
||||||
SpaceAroundPointerQualifiers: After
|
|
||||||
SpaceBeforeAssignmentOperators: true
|
|
||||||
SpaceBeforeCaseColon: false
|
|
||||||
SpaceBeforeCpp11BracedList: false
|
|
||||||
SpaceBeforeCtorInitializerColon: false
|
|
||||||
SpaceBeforeInheritanceColon: false
|
|
||||||
SpaceBeforeParens: ControlStatementsExceptControlMacros
|
|
||||||
SpaceBeforeRangeBasedForLoopColon: true
|
|
||||||
SpaceBeforeSquareBrackets: false
|
|
||||||
SpaceInEmptyBlock: false
|
|
||||||
SpaceInEmptyParentheses: false
|
|
||||||
SpacesInAngles: Never
|
|
||||||
SpacesInCStyleCastParentheses: false
|
|
||||||
SpacesInConditionalStatement: false
|
|
||||||
SpacesInContainerLiterals: false
|
|
||||||
SpacesInLineCommentPrefix:
|
|
||||||
Minimum: 0
|
|
||||||
Maximum: -1
|
|
||||||
SpacesInParentheses: false
|
|
||||||
SpacesInSquareBrackets: false
|
|
||||||
Standard: c++20
|
|
||||||
StatementAttributeLikeMacros: []
|
|
||||||
StatementMacros: []
|
|
||||||
TabWidth: 4
|
|
||||||
TypenameMacros: []
|
|
||||||
UseCRLF: false # wtf
|
|
||||||
UseTab: Never
|
|
||||||
WhitespaceSensitiveMacros: ["BOOST_PP_STRINGSIZE"]
|
|
|
@ -1,2 +1,3 @@
|
||||||
build
|
zig-out/
|
||||||
.cache
|
zig-cache/
|
||||||
|
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
cmake_minimum_required(VERSION 3.0.0)
|
|
||||||
project(PORTINGTOOLS)
|
|
||||||
|
|
||||||
file(GLOB_RECURSE sources src/*)
|
|
||||||
|
|
||||||
find_package(Boost 1.80 COMPONENTS log filesystem REQUIRED)
|
|
||||||
|
|
||||||
include_directories(${Boost_INCLUDE_DIR})
|
|
||||||
|
|
||||||
add_executable(portingtools ${sources})
|
|
||||||
|
|
||||||
set_property(TARGET portingtools PROPERTY CXX_STANDARD 20)
|
|
||||||
|
|
||||||
target_link_libraries(portingtools LINK_PUBLIC ${Boost_LIBRARIES})
|
|
10
README.md
10
README.md
|
@ -2,12 +2,8 @@
|
||||||
|
|
||||||
This tool is meant to make porting mods easier for MineteckReloaded.
|
This tool is meant to make porting mods easier for MineteckReloaded.
|
||||||
|
|
||||||
Also, it helped me learn some c++, so don't expect good code.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
- `cmake -S. -Bbuild`
|
- `zig build`
|
||||||
- `cmake --build build`
|
- start the server with `zig-out/bin/portingtools serve`
|
||||||
- start the server with `build/portingtools serve`
|
- map stuff with `zig-out/bin/portingtools map`
|
||||||
- map stuff with `build/portingtools map`
|
|
||||||
- resolve ResourceLocations with `build/portingtools resourceloc`
|
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub fn build(b: *std.build.Builder) void {
|
||||||
|
// Standard target options allows the person running `zig build` to choose
|
||||||
|
// what target to build for. Here we do not override the defaults, which
|
||||||
|
// means any target is allowed, and the default is native. Other options
|
||||||
|
// for restricting supported target set are available.
|
||||||
|
const target = b.standardTargetOptions(.{});
|
||||||
|
|
||||||
|
// Standard release options allow the person running `zig build` to select
|
||||||
|
// between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall.
|
||||||
|
const mode = b.standardReleaseOptions();
|
||||||
|
|
||||||
|
const exe = b.addExecutable("portingtools", "src/main.zig");
|
||||||
|
exe.setTarget(target);
|
||||||
|
exe.setBuildMode(mode);
|
||||||
|
exe.install();
|
||||||
|
|
||||||
|
const run_cmd = exe.run();
|
||||||
|
run_cmd.step.dependOn(b.getInstallStep());
|
||||||
|
if (b.args) |args| {
|
||||||
|
run_cmd.addArgs(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const run_step = b.step("run", "Run the app");
|
||||||
|
run_step.dependOn(&run_cmd.step);
|
||||||
|
|
||||||
|
const exe_tests = b.addTest("src/main.zig");
|
||||||
|
exe_tests.setTarget(target);
|
||||||
|
exe_tests.setBuildMode(mode);
|
||||||
|
|
||||||
|
const test_step = b.step("test", "Run unit tests");
|
||||||
|
test_step.dependOn(&exe_tests.step);
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
str: []const u8,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn read(reader: anytype, alloc: std.mem.Allocator) !Self {
|
||||||
|
const strlen = try reader.readIntBig(u32);
|
||||||
|
|
||||||
|
const str = try alloc.alloc(u8, strlen);
|
||||||
|
errdefer alloc.free(str);
|
||||||
|
|
||||||
|
try reader.readNoEof(str);
|
||||||
|
|
||||||
|
return .{ .str = str };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(self: *const Self, writer: anytype) !void {
|
||||||
|
try writer.writeIntBig(u32, @intCast(u32, self.str.len));
|
||||||
|
try writer.writeAll(self.str);
|
||||||
|
}
|
13
src/ansi.hpp
13
src/ansi.hpp
|
@ -1,13 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define ANSI_CODE_DEF inline const char*
|
|
||||||
|
|
||||||
namespace ansi {
|
|
||||||
ANSI_CODE_DEF reset = "\033[0m";
|
|
||||||
|
|
||||||
ANSI_CODE_DEF cyan = "\033[0;36m";
|
|
||||||
ANSI_CODE_DEF green = "\033[0;32m";
|
|
||||||
ANSI_CODE_DEF yellow = "\033[0;33m";
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef ANSI_CODE_DEF
|
|
|
@ -1,61 +0,0 @@
|
||||||
#include "data.hpp"
|
|
||||||
#include "resourceloc.hpp"
|
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <cerrno>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/msg.h>
|
|
||||||
|
|
||||||
namespace client {
|
|
||||||
void sendMsg(int msqid, data::RequestMapMsg* msg) {
|
|
||||||
if (msgsnd(msqid, msg, sizeof(data::RequestMapMsg) - sizeof(long), 0) < 0) {
|
|
||||||
throw std::runtime_error(std::string("send error ") + strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string recvMsg(int msqid) {
|
|
||||||
data::ResponseMapMsg msg;
|
|
||||||
|
|
||||||
if (msgrcv(
|
|
||||||
msqid,
|
|
||||||
&msg,
|
|
||||||
sizeof(data::ResponseMapMsg) - sizeof(long),
|
|
||||||
data::clientbound_msg,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
< 0) {
|
|
||||||
throw std::runtime_error(std::string("receive error ") + strerror(errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
return std::string(&msg.data[0], msg.datalen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void map(key_t key) {
|
|
||||||
std::string inp;
|
|
||||||
std::cin >> inp;
|
|
||||||
|
|
||||||
auto msqid = msgget(key, 0664);
|
|
||||||
|
|
||||||
if (msqid < 0) {
|
|
||||||
throw std::runtime_error("msgget fail. is the server running?");
|
|
||||||
}
|
|
||||||
|
|
||||||
data::RequestMapMsg req;
|
|
||||||
req.msgtype = data::serverbound_msg;
|
|
||||||
req.datalen = inp.size();
|
|
||||||
std::copy(inp.begin(), inp.end(), req.data);
|
|
||||||
|
|
||||||
sendMsg(msqid, &req);
|
|
||||||
std::cout << recvMsg(msqid) << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void resolveResourceLoc(key_t key) {
|
|
||||||
std::string inp;
|
|
||||||
std::cin >> inp;
|
|
||||||
|
|
||||||
std::cout << resourceloc::resolve(inp) << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,8 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
namespace client {
|
|
||||||
void map(key_t key);
|
|
||||||
void resolveResourceLoc(key_t key);
|
|
||||||
}
|
|
91
src/csv.cpp
91
src/csv.cpp
|
@ -1,91 +0,0 @@
|
||||||
#include "csv.hpp"
|
|
||||||
|
|
||||||
#include <istream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace csv {
|
|
||||||
enum class CSVState {
|
|
||||||
UnquotedField,
|
|
||||||
QuotedField,
|
|
||||||
QuotedQuote
|
|
||||||
};
|
|
||||||
|
|
||||||
// this function has been made in collaboration with
|
|
||||||
// StackOverflow https://stackoverflow.com/a/30338543
|
|
||||||
void readCSVRow(const std::string& row, std::vector<std::string>& fields) {
|
|
||||||
fields.push_back("");
|
|
||||||
CSVState state = CSVState::UnquotedField;
|
|
||||||
size_t i = 0; // index of the current field
|
|
||||||
|
|
||||||
for (char c : row) {
|
|
||||||
switch (state) {
|
|
||||||
case CSVState::UnquotedField:
|
|
||||||
switch (c) {
|
|
||||||
case ',': // end of field
|
|
||||||
fields.push_back("");
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '"':
|
|
||||||
state = CSVState::QuotedField;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fields[i].push_back(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CSVState::QuotedField:
|
|
||||||
switch (c) {
|
|
||||||
case '"':
|
|
||||||
state = CSVState::QuotedQuote;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
fields[i].push_back(c);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case CSVState::QuotedQuote:
|
|
||||||
switch (c) {
|
|
||||||
case ',': // , after closing quote
|
|
||||||
fields.push_back("");
|
|
||||||
i++;
|
|
||||||
state = CSVState::UnquotedField;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '"': // "" -> "
|
|
||||||
fields[i].push_back('"');
|
|
||||||
state = CSVState::QuotedField;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // end of quote
|
|
||||||
state = CSVState::UnquotedField;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CsvReader::operator>>(std::vector<std::string>& line) {
|
|
||||||
line.clear();
|
|
||||||
|
|
||||||
if (!m_stream) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string row;
|
|
||||||
std::getline(m_stream, row);
|
|
||||||
|
|
||||||
readCSVRow(row, line);
|
|
||||||
|
|
||||||
return !!m_stream;
|
|
||||||
}
|
|
||||||
}
|
|
17
src/csv.hpp
17
src/csv.hpp
|
@ -1,17 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <fstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace csv {
|
|
||||||
class CsvReader {
|
|
||||||
private:
|
|
||||||
std::ifstream m_stream;
|
|
||||||
|
|
||||||
public:
|
|
||||||
CsvReader(auto stream): m_stream(stream){};
|
|
||||||
~CsvReader() = default;
|
|
||||||
|
|
||||||
bool operator>>(std::vector<std::string>& line);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const Record = struct {
|
||||||
|
cols: [][]u8,
|
||||||
|
|
||||||
|
pub fn deinit(self: Record, alloc: std.mem.Allocator) void {
|
||||||
|
for (self.cols) |col| {
|
||||||
|
alloc.free(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
alloc.free(self.cols);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn csvReader(reader: anytype) CsvReader(@TypeOf(reader)) {
|
||||||
|
return .{ .reader = reader };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn CsvReader(comptime Reader: type) type {
|
||||||
|
return struct {
|
||||||
|
reader: Reader,
|
||||||
|
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
pub fn next(self: *Self, alloc: std.mem.Allocator) !?Record {
|
||||||
|
var prev_quote = false;
|
||||||
|
var in_quote = false;
|
||||||
|
|
||||||
|
var cols = std.ArrayList([]u8).init(alloc);
|
||||||
|
defer cols.deinit();
|
||||||
|
|
||||||
|
var buf = std.ArrayList(u8).init(alloc);
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
var ch_buf: [1]u8 = undefined;
|
||||||
|
|
||||||
|
if (try self.reader.read(&ch_buf) == 0) {
|
||||||
|
if (buf.items.len != 0) {
|
||||||
|
try cols.append(try buf.toOwnedSlice());
|
||||||
|
return Record{ .cols = try cols.toOwnedSlice() };
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ch = ch_buf[0];
|
||||||
|
|
||||||
|
switch (ch) {
|
||||||
|
'"' => {
|
||||||
|
if (prev_quote) {
|
||||||
|
try buf.append('"');
|
||||||
|
} else {
|
||||||
|
prev_quote = true;
|
||||||
|
in_quote = !in_quote;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
',' => {
|
||||||
|
prev_quote = false;
|
||||||
|
if (in_quote) {
|
||||||
|
try buf.append(',');
|
||||||
|
} else {
|
||||||
|
defer buf.clearRetainingCapacity();
|
||||||
|
try cols.append(try alloc.dupe(u8, buf.items));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'\n' => {
|
||||||
|
try cols.append(try buf.toOwnedSlice());
|
||||||
|
return Record{ .cols = try cols.toOwnedSlice() };
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
prev_quote = false;
|
||||||
|
try buf.append(ch);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
18
src/data.cpp
18
src/data.cpp
|
@ -1,18 +0,0 @@
|
||||||
#include "data.hpp"
|
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
|
|
||||||
namespace data {
|
|
||||||
key_t getIpcKeyFromExeName(char* argv0) {
|
|
||||||
auto k = ftok(argv0, 'X');
|
|
||||||
|
|
||||||
if (k < 0) {
|
|
||||||
throw std::runtime_error("failed to get IPC key");
|
|
||||||
}
|
|
||||||
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
}
|
|
24
src/data.hpp
24
src/data.hpp
|
@ -1,24 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
namespace data {
|
|
||||||
const int serverbound_msg = 1;
|
|
||||||
const int clientbound_msg = 2;
|
|
||||||
|
|
||||||
key_t getIpcKeyFromExeName(char* argv0);
|
|
||||||
|
|
||||||
struct RequestMapMsg {
|
|
||||||
long msgtype;
|
|
||||||
|
|
||||||
unsigned int datalen;
|
|
||||||
char data[128];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ResponseMapMsg {
|
|
||||||
long msgtype;
|
|
||||||
|
|
||||||
unsigned int datalen;
|
|
||||||
char data[128];
|
|
||||||
};
|
|
||||||
}
|
|
33
src/main.cpp
33
src/main.cpp
|
@ -1,33 +0,0 @@
|
||||||
#include "client.hpp"
|
|
||||||
#include "data.hpp"
|
|
||||||
#include "server.hpp"
|
|
||||||
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <cstring>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
void main2(int argc, char* argv[]) {
|
|
||||||
if (argc < 2) {
|
|
||||||
throw std::runtime_error("not enough arguments!");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strcmp(argv[1], "serve") == 0) {
|
|
||||||
server::run(data::getIpcKeyFromExeName(argv[0]));
|
|
||||||
} else if (strcmp(argv[1], "map") == 0) {
|
|
||||||
client::map(data::getIpcKeyFromExeName(argv[0]));
|
|
||||||
} else if (strcmp(argv[1], "resourceloc") == 0) {
|
|
||||||
client::resolveResourceLoc(data::getIpcKeyFromExeName(argv[0]));
|
|
||||||
} else {
|
|
||||||
throw std::runtime_error("unknown command!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
try {
|
|
||||||
main2(argc, argv);
|
|
||||||
return 0;
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
BOOST_LOG_TRIVIAL(fatal) << "Exception: " << e.what();
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,179 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const csv_reader = @import("csv_reader.zig");
|
||||||
|
|
||||||
|
const StringPacket = @import("StringPacket.zig");
|
||||||
|
|
||||||
|
pub const log_level = .debug;
|
||||||
|
|
||||||
|
const Mapping = struct {
|
||||||
|
mapped: []const u8,
|
||||||
|
doc: ?[]const u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn getAddr(alloc: std.mem.Allocator) !std.net.Address {
|
||||||
|
const sockpath = try std.fs.path.join(alloc, &.{
|
||||||
|
std.os.getenv("XDG_RUNTIME_DIR") orelse return error.MissingRuntimeDir,
|
||||||
|
"portingtools.sock",
|
||||||
|
});
|
||||||
|
defer alloc.free(sockpath);
|
||||||
|
|
||||||
|
return try std.net.Address.initUnix(sockpath);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() !void {
|
||||||
|
if (std.os.argv.len < 2) {
|
||||||
|
return error.InvalidArgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cmd = std.mem.span(std.os.argv[1]);
|
||||||
|
|
||||||
|
if (std.mem.eql(u8, cmd, "serve")) {
|
||||||
|
try runServer();
|
||||||
|
} else if (std.mem.eql(u8, cmd, "map")) {
|
||||||
|
try map();
|
||||||
|
} else {
|
||||||
|
return error.InvalidCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn map() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
const stdin = std.io.getStdIn().reader();
|
||||||
|
|
||||||
|
var buf: [128]u8 = undefined;
|
||||||
|
const request = (try stdin.readUntilDelimiterOrEof(&buf, '\n')) orelse return error.NoInput;
|
||||||
|
|
||||||
|
const addr = try getAddr(alloc);
|
||||||
|
|
||||||
|
const sockfd = try std.os.socket(
|
||||||
|
std.os.AF.UNIX,
|
||||||
|
std.os.SOCK.STREAM | std.os.SOCK.CLOEXEC,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
defer std.os.closeSocket(sockfd);
|
||||||
|
|
||||||
|
try std.os.connect(sockfd, &addr.any, addr.getOsSockLen());
|
||||||
|
|
||||||
|
const stream = std.net.Stream{ .handle = sockfd };
|
||||||
|
|
||||||
|
const pkt = StringPacket{ .str = std.mem.trim(u8, request, &std.ascii.whitespace) };
|
||||||
|
try pkt.write(stream.writer());
|
||||||
|
|
||||||
|
const res = try StringPacket.read(stream.reader(), alloc);
|
||||||
|
defer alloc.free(res.str);
|
||||||
|
|
||||||
|
try std.io.getStdOut().writer().print("{s}\n", .{res.str});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn runServer() !void {
|
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
|
defer _ = gpa.deinit();
|
||||||
|
|
||||||
|
const alloc = gpa.allocator();
|
||||||
|
|
||||||
|
var mappings = std.StringHashMap(Mapping).init(alloc);
|
||||||
|
defer mappings.deinit();
|
||||||
|
|
||||||
|
var data_arena = std.heap.ArenaAllocator.init(alloc);
|
||||||
|
defer data_arena.deinit();
|
||||||
|
|
||||||
|
var renames = std.StringHashMap([]const u8).init(alloc);
|
||||||
|
defer renames.deinit();
|
||||||
|
|
||||||
|
if (std.fs.cwd().openFile("renames.csv", .{})) |renames_file| {
|
||||||
|
defer renames_file.close();
|
||||||
|
|
||||||
|
var reader = csv_reader.csvReader(std.io.bufferedReader(renames_file.reader()));
|
||||||
|
|
||||||
|
while (try reader.next(data_arena.allocator())) |rec| {
|
||||||
|
if (rec.cols.len != 2) {
|
||||||
|
std.log.warn("found rename with invalid record length, skipping", .{});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (renames.contains(rec.cols[0])) {
|
||||||
|
std.log.warn("duplicate rename '{s}'", .{rec.cols[0]});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try renames.put(rec.cols[0], rec.cols[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
std.log.info("loaded {} renames", .{renames.count()});
|
||||||
|
} else |err| {
|
||||||
|
std.log.warn("couldn't open renames file: {}, skipping", .{err});
|
||||||
|
}
|
||||||
|
|
||||||
|
var mappings_dir = try std.fs.cwd().openIterableDir("mappings", .{});
|
||||||
|
defer mappings_dir.close();
|
||||||
|
|
||||||
|
var mappings_iter = mappings_dir.iterate();
|
||||||
|
|
||||||
|
while (try mappings_iter.next()) |entry| {
|
||||||
|
const fpath = try std.fs.path.join(alloc, &.{ "mappings", entry.name });
|
||||||
|
defer alloc.free(fpath);
|
||||||
|
|
||||||
|
var mapfile = try std.fs.cwd().openFile(fpath, .{});
|
||||||
|
defer mapfile.close();
|
||||||
|
|
||||||
|
var reader = csv_reader.csvReader(std.io.bufferedReader(mapfile.reader()));
|
||||||
|
|
||||||
|
while (try reader.next(data_arena.allocator())) |rec| {
|
||||||
|
if (rec.cols.len < 2) {
|
||||||
|
std.log.warn("found mapping with invalid length, skipping", .{});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mappings.contains(rec.cols[0])) {
|
||||||
|
std.log.warn("duplicate mapping '{s}'", .{rec.cols[0]});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapping = Mapping{
|
||||||
|
.mapped = rec.cols[1],
|
||||||
|
.doc = if (rec.cols.len >= 4) rec.cols[3] else null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try mappings.put(rec.cols[0], mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std.log.info("loaded {} mappings", .{mappings.count()});
|
||||||
|
|
||||||
|
var server = std.net.StreamServer.init(.{});
|
||||||
|
defer server.deinit();
|
||||||
|
|
||||||
|
const addr = try getAddr(alloc);
|
||||||
|
try server.listen(addr);
|
||||||
|
std.log.info("listening on {}", .{addr});
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const con = try server.accept();
|
||||||
|
const req = try StringPacket.read(con.stream.reader(), alloc);
|
||||||
|
defer alloc.free(req.str);
|
||||||
|
|
||||||
|
if (mappings.get(req.str)) |mapping| {
|
||||||
|
const res = StringPacket{ .str = mapping.mapped };
|
||||||
|
try res.write(con.stream.writer());
|
||||||
|
|
||||||
|
std.log.info(
|
||||||
|
\\
|
||||||
|
\\ Unmapped: {s}
|
||||||
|
\\ Mapped: {s}
|
||||||
|
\\
|
||||||
|
\\ Doc: {s}
|
||||||
|
, .{
|
||||||
|
req.str,
|
||||||
|
mapping.mapped,
|
||||||
|
mapping.doc orelse "<no docs>",
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const res = StringPacket{ .str = "<unknown>" };
|
||||||
|
try res.write(con.stream.writer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/mappings.cpp
121
src/mappings.cpp
|
@ -1,121 +0,0 @@
|
||||||
#include "mappings.hpp"
|
|
||||||
|
|
||||||
#include "ansi.hpp"
|
|
||||||
#include "csv.hpp"
|
|
||||||
|
|
||||||
#include <boost/algorithm/string.hpp>
|
|
||||||
#include <boost/algorithm/string/classification.hpp>
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
#include <boost/filesystem.hpp>
|
|
||||||
#include <boost/filesystem/directory.hpp>
|
|
||||||
#include <boost/filesystem/exception.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <boost/system/detail/errc.hpp>
|
|
||||||
#include <fstream>
|
|
||||||
#include <optional>
|
|
||||||
#include <ostream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace fs = boost::filesystem;
|
|
||||||
|
|
||||||
namespace mappings {
|
|
||||||
Mappings Mappings::load() {
|
|
||||||
std::map<std::string, Mapping> mappings;
|
|
||||||
std::map<std::string, std::string> renames;
|
|
||||||
|
|
||||||
// load mappings
|
|
||||||
fs::directory_iterator end_itr;
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (fs::directory_iterator itr("mappings"); itr != end_itr; ++itr) {
|
|
||||||
if (fs::is_directory(itr->status())
|
|
||||||
|| !boost::algorithm::ends_with(itr->path().string(), ".csv"))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
csv::CsvReader csv(itr->path().string());
|
|
||||||
|
|
||||||
std::vector<std::string> l;
|
|
||||||
|
|
||||||
while (csv >> l) {
|
|
||||||
if (l.size() < 2) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "found invalid mapping";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
mappings[l[0]] = {
|
|
||||||
l[0], l[1], l.size() >= 4 ? std::optional(l[3]) : std::nullopt
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (boost::filesystem::filesystem_error& e) {
|
|
||||||
if (e.code() == boost::system::errc::no_such_file_or_directory) {
|
|
||||||
throw std::runtime_error(
|
|
||||||
std::string("The mappings directory is missing!\n") + e.what()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fs::is_regular_file("renames.csv")) {
|
|
||||||
csv::CsvReader csv("renames.csv");
|
|
||||||
|
|
||||||
std::vector<std::string> l;
|
|
||||||
|
|
||||||
while (csv >> l) {
|
|
||||||
if (l.size() < 2) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "found invalid rename";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
renames[l[0]] = l[1];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "no renames found, skipping";
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "loaded " << mappings.size() << " mappings and "
|
|
||||||
<< renames.size() << " renames";
|
|
||||||
return { mappings, renames };
|
|
||||||
}
|
|
||||||
|
|
||||||
void showMapInfo(Mapping mapping, std::optional<std::string> rename) {
|
|
||||||
auto doc = mapping.doc.has_value()
|
|
||||||
? std::string(ansi::yellow) + "\n\n\t" + *mapping.doc + "\n"
|
|
||||||
: "";
|
|
||||||
|
|
||||||
if (rename.has_value()) {
|
|
||||||
BOOST_LOG_TRIVIAL(info)
|
|
||||||
<< "\nFound mapping:\n"
|
|
||||||
<< ansi::cyan << "\n\tOriginal\t" << ansi::green << mapping.orig
|
|
||||||
<< ansi::cyan << "\n\tRemapped\t" << ansi::green << mapping.name
|
|
||||||
<< ansi::cyan << "\n\tRenamed \t" << ansi::green << *rename << doc
|
|
||||||
<< ansi::reset;
|
|
||||||
} else {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "\nFound mapping:\n"
|
|
||||||
<< ansi::cyan << "\n\tOriginal\t" << ansi::green
|
|
||||||
<< mapping.orig << ansi::cyan << "\n\tRemapped\t"
|
|
||||||
<< ansi::green << mapping.name << doc << ansi::reset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Mappings::map(std::string inp) {
|
|
||||||
if (!this->m_mappings.count(inp)) {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "unknown mapping '" << inp << "'";
|
|
||||||
return "<unknown>";
|
|
||||||
}
|
|
||||||
|
|
||||||
auto mapped = this->m_mappings[inp];
|
|
||||||
|
|
||||||
if (this->m_renames.count(mapped.name)) {
|
|
||||||
BOOST_LOG_TRIVIAL(info)
|
|
||||||
<< "found rename " << mapped.name << " -> " << m_renames[mapped.name];
|
|
||||||
showMapInfo(mapped, m_renames[mapped.name]);
|
|
||||||
return m_renames[mapped.name];
|
|
||||||
}
|
|
||||||
|
|
||||||
showMapInfo(mapped, std::nullopt);
|
|
||||||
return mapped.name;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <map>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace mappings {
|
|
||||||
struct Mapping {
|
|
||||||
std::string orig;
|
|
||||||
std::string name;
|
|
||||||
std::optional<std::string> doc;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Mappings {
|
|
||||||
std::map<std::string, Mapping> m_mappings;
|
|
||||||
std::map<std::string, std::string> m_renames;
|
|
||||||
|
|
||||||
public:
|
|
||||||
Mappings(
|
|
||||||
std::map<std::string, Mapping> mappings,
|
|
||||||
std::map<std::string, std::string> renames
|
|
||||||
):
|
|
||||||
m_mappings(mappings), m_renames(renames) {}
|
|
||||||
|
|
||||||
~Mappings() = default;
|
|
||||||
static Mappings load();
|
|
||||||
std::string map(std::string inp);
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,39 +0,0 @@
|
||||||
#include "resourceloc.hpp"
|
|
||||||
|
|
||||||
#include <boost/algorithm/string/split.hpp>
|
|
||||||
#include <boost/algorithm/string/trim.hpp>
|
|
||||||
#include <boost/format.hpp>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <iostream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace resourceloc {
|
|
||||||
std::string resolve(std::string inp) {
|
|
||||||
boost::algorithm::trim_if(inp, boost::is_any_of(" \n\r\""));
|
|
||||||
|
|
||||||
std::vector<std::string> components;
|
|
||||||
boost::algorithm::split(components, inp, boost::is_any_of("/"));
|
|
||||||
|
|
||||||
if (components.size() < 3 || components[0] != "" || components[1] != "mods") {
|
|
||||||
BOOST_LOG_TRIVIAL(warning) << "invalid resourceloc";
|
|
||||||
return "<invalid>";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string path_component;
|
|
||||||
|
|
||||||
for (unsigned int i = 3; i < components.size(); i++) {
|
|
||||||
path_component += components[i];
|
|
||||||
|
|
||||||
if (i != components.size() - 1) {
|
|
||||||
path_component += "/";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return boost::str(
|
|
||||||
boost::format("new ResourceLocation(\"%1%\", \"%2%\")") % components[2]
|
|
||||||
% path_component
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace resourceloc {
|
|
||||||
std::string resolve(std::string inp);
|
|
||||||
}
|
|
|
@ -1,60 +0,0 @@
|
||||||
#include "server.hpp"
|
|
||||||
|
|
||||||
#include "data.hpp"
|
|
||||||
#include "mappings.hpp"
|
|
||||||
#include "resourceloc.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <boost/log/trivial.hpp>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/msg.h>
|
|
||||||
|
|
||||||
namespace server {
|
|
||||||
void sendToClient(int msqid, std::string msg) {
|
|
||||||
data::ResponseMapMsg pkt;
|
|
||||||
pkt.msgtype = data::clientbound_msg;
|
|
||||||
pkt.datalen = msg.size();
|
|
||||||
std::copy(msg.begin(), msg.end(), pkt.data);
|
|
||||||
|
|
||||||
if (msgsnd(msqid, &pkt, sizeof(data::ResponseMapMsg) - sizeof(long), 0) < 0) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "failed to send response " << strerror(errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void run(key_t key) {
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "starting server";
|
|
||||||
|
|
||||||
auto msqid = msgget(key, 0664 | IPC_CREAT);
|
|
||||||
|
|
||||||
if (msqid < 0) {
|
|
||||||
throw std::runtime_error("msgget fail");
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "msqid " << msqid;
|
|
||||||
|
|
||||||
auto mappings = mappings::Mappings::load();
|
|
||||||
|
|
||||||
data::RequestMapMsg msg;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (msgrcv(
|
|
||||||
msqid,
|
|
||||||
&msg,
|
|
||||||
sizeof(data::RequestMapMsg) - sizeof(long),
|
|
||||||
data::serverbound_msg,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
< 0) {
|
|
||||||
BOOST_LOG_TRIVIAL(error) << "receive error " << strerror(errno);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string s(msg.data, msg.datalen);
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "got request to map '" << s << "'";
|
|
||||||
sendToClient(msqid, mappings.map(s));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
#pragma once
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
namespace server {
|
|
||||||
void run(key_t key);
|
|
||||||
}
|
|
Loading…
Reference in New Issue