initial commit
This commit is contained in:
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
|
||||||
|
|
||||||
|
build/
|
||||||
|
cmake-build-*/
|
||||||
|
.cmake/
|
||||||
|
CMakeFiles/
|
||||||
|
CMakeScripts/
|
||||||
|
CMakeCache.txt
|
||||||
|
cmake_install.cmake
|
||||||
|
Testing/
|
||||||
|
.ninja*
|
||||||
|
build.ninja
|
||||||
|
|
||||||
|
Makefile
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
*.o
|
||||||
|
*.a
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.28)
|
||||||
|
project(NandPartitionTableConvert)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_C_STANDARD 11)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC -Werror=delete-non-virtual-dtor -Werror=return-type -Werror=non-virtual-dtor -Wno-invalid-offsetof")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC -Werror=return-type")
|
||||||
|
|
||||||
|
add_executable(NandPartitionTableConvert
|
||||||
|
nand_part_table_cvt.cc
|
||||||
|
os_utils.cc
|
||||||
|
utf_convert.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_subdirectory(fmt fmt)
|
||||||
|
target_compile_definitions(fmt-header-only INTERFACE FMT_STATIC_THOUSANDS_SEPARATOR=1)
|
||||||
|
|
||||||
|
set_target_properties(NandPartitionTableConvert PROPERTIES
|
||||||
|
CXX_EXTENSIONS OFF
|
||||||
|
POSITION_INDEPENDENT_CODE ON
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_options(NandPartitionTableConvert PRIVATE "-static" "-static-pie" "-static-libgcc" "-static-libstdc++")
|
||||||
|
|
||||||
|
target_link_libraries(NandPartitionTableConvert fmt::fmt-header-only)
|
||||||
328
nand_part_table_cvt.cc
Normal file
328
nand_part_table_cvt.cc
Normal file
@@ -0,0 +1,328 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "os_utils.h"
|
||||||
|
#include "utf_convert.h"
|
||||||
|
|
||||||
|
constexpr uint32_t kInvalidPartition = 0xFFFFFFFFu;
|
||||||
|
|
||||||
|
struct LeapPartitionItem {
|
||||||
|
uint32_t fixed = 1;
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t end = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MinatoPartitionItem {
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t end = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
uint32_t flags = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct XeltekPartitionItem {
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t end = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
uint32_t unknown = kInvalidPartition;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
enum class PartitionType {
|
||||||
|
kNone = 0,
|
||||||
|
kLeap,
|
||||||
|
kMinato,
|
||||||
|
kXeltek
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(MinatoPartitionItem) == 16 &&
|
||||||
|
sizeof(XeltekPartitionItem) == 16 &&
|
||||||
|
sizeof(LeapPartitionItem) == 16);
|
||||||
|
|
||||||
|
struct SimplePartitionItem {
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t end = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
PartitionType ReadPartitionTable(const std::vector<uint8_t>& data, std::vector<SimplePartitionItem>& out) {
|
||||||
|
if (data.size() % 16 != 0) {
|
||||||
|
fmt::print("Invalid partition table size\n");
|
||||||
|
return PartitionType::kNone;
|
||||||
|
}
|
||||||
|
if (data.empty()) {
|
||||||
|
fmt::print("Empty partition table\n");
|
||||||
|
return PartitionType::kNone;
|
||||||
|
}
|
||||||
|
out.clear();
|
||||||
|
out.reserve(data.size() / 16);
|
||||||
|
bool isLeap = false;
|
||||||
|
const std::array<char, 16> kLeapHeader{"GROUP DEFINE2\0\0"};
|
||||||
|
if (memcmp(data.data(), kLeapHeader.data(), 16) == 0) {
|
||||||
|
isLeap = true;
|
||||||
|
}
|
||||||
|
if (isLeap) {
|
||||||
|
const auto* items = reinterpret_cast<const LeapPartitionItem*>(data.data());
|
||||||
|
// start from 1
|
||||||
|
for (size_t i = 1; i < data.size() / 16; i++) {
|
||||||
|
const auto& item = items[i];
|
||||||
|
if (item.start == kInvalidPartition) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (item.fixed != 1) {
|
||||||
|
fmt::print("Invalid partition table, got fixed value {}\n", item.fixed);
|
||||||
|
return PartitionType::kNone;
|
||||||
|
}
|
||||||
|
SimplePartitionItem simpleItem;
|
||||||
|
simpleItem.start = item.start;
|
||||||
|
simpleItem.end = item.end;
|
||||||
|
simpleItem.size = item.size;
|
||||||
|
out.push_back(simpleItem);
|
||||||
|
}
|
||||||
|
return PartitionType::kLeap;
|
||||||
|
} else {
|
||||||
|
struct UnknownPartitionItem {
|
||||||
|
uint32_t start = 0;
|
||||||
|
uint32_t end = 0;
|
||||||
|
uint32_t size = 0;
|
||||||
|
uint32_t unknown = 0;
|
||||||
|
};
|
||||||
|
PartitionType type = PartitionType::kNone;
|
||||||
|
const auto* items = reinterpret_cast<const UnknownPartitionItem*>(data.data());
|
||||||
|
if (items[0].unknown == 0 || items[0].unknown == 1) {
|
||||||
|
type = PartitionType::kMinato;
|
||||||
|
} else if (items[0].unknown == kInvalidPartition) {
|
||||||
|
type = PartitionType::kXeltek;
|
||||||
|
} else {
|
||||||
|
fmt::print("Invalid partition table, unknown value {}\n", items[0].unknown);
|
||||||
|
return PartitionType::kNone;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < data.size() / 16; i++) {
|
||||||
|
const auto& item = items[i];
|
||||||
|
if (item.start == kInvalidPartition) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// check unknown
|
||||||
|
if (item.unknown != kInvalidPartition && item.unknown != 0 && item.unknown != 1) {
|
||||||
|
fmt::print("Invalid partition table, got unknown value {}\n", item.unknown);
|
||||||
|
return PartitionType::kNone;
|
||||||
|
}
|
||||||
|
SimplePartitionItem simpleItem;
|
||||||
|
simpleItem.start = item.start;
|
||||||
|
simpleItem.end = item.end;
|
||||||
|
simpleItem.size = item.size;
|
||||||
|
out.push_back(simpleItem);
|
||||||
|
}
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> GenerateLeapPartitionTable(const std::vector<SimplePartitionItem>& items) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
data.resize((items.size() + 2) * 16);
|
||||||
|
std::array<char, 16> header{"GROUP DEFINE2\0\0"};
|
||||||
|
memcpy(data.data(), header.data(), 16);
|
||||||
|
auto* outItems = reinterpret_cast<LeapPartitionItem*>(data.data());
|
||||||
|
for (size_t i = 0; i < items.size(); i++) {
|
||||||
|
const auto& item = items[i];
|
||||||
|
outItems[i + 1].fixed = 1;
|
||||||
|
outItems[i + 1].start = item.start;
|
||||||
|
outItems[i + 1].end = item.end;
|
||||||
|
outItems[i + 1].size = item.size;
|
||||||
|
}
|
||||||
|
// add end
|
||||||
|
outItems[items.size() + 1].fixed = kInvalidPartition;
|
||||||
|
outItems[items.size() + 1].start = kInvalidPartition;
|
||||||
|
outItems[items.size() + 1].end = kInvalidPartition;
|
||||||
|
outItems[items.size() + 1].size = kInvalidPartition;
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> GenerateXeltekMinatoPartitionTable(const std::vector<SimplePartitionItem>& items,
|
||||||
|
PartitionType type) {
|
||||||
|
std::vector<uint8_t> data;
|
||||||
|
data.resize(items.size() * 16);
|
||||||
|
auto* outItems = reinterpret_cast<MinatoPartitionItem*>(data.data());
|
||||||
|
for (size_t i = 0; i < items.size(); i++) {
|
||||||
|
const auto& item = items[i];
|
||||||
|
outItems[i].start = item.start;
|
||||||
|
outItems[i].end = item.end;
|
||||||
|
outItems[i].size = item.size;
|
||||||
|
if (type == PartitionType::kMinato) {
|
||||||
|
outItems[i].flags = 0;
|
||||||
|
} else if (type == PartitionType::kXeltek) {
|
||||||
|
outItems[i].flags = kInvalidPartition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> GeneratePartitionTable(const std::vector<SimplePartitionItem>& items, PartitionType type) {
|
||||||
|
if (type == PartitionType::kLeap) {
|
||||||
|
return GenerateLeapPartitionTable(items);
|
||||||
|
} else {
|
||||||
|
return GenerateXeltekMinatoPartitionTable(items, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* GetPartionTypeLongName(PartitionType type) {
|
||||||
|
switch (type) {
|
||||||
|
case PartitionType::kLeap:
|
||||||
|
return "Leap Electronic NAND Flash programmer";
|
||||||
|
case PartitionType::kMinato:
|
||||||
|
return "Minato NAND Flash programmer, Data I/O NAND Flash programmer";
|
||||||
|
case PartitionType::kXeltek:
|
||||||
|
return "Xeltek Universal IC Chip Programmer";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int MainEntry(int argc, char** argv) {
|
||||||
|
PartitionType inputType = PartitionType::kNone;
|
||||||
|
PartitionType outputType = PartitionType::kNone;
|
||||||
|
std::string inputFile;
|
||||||
|
std::string outputFile;
|
||||||
|
bool quiet = false;
|
||||||
|
if (argc == 1 || (argc == 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0))) {
|
||||||
|
fmt::print("Usage: {} -i <input> [-q] [-L/M/X output]\n", argv[0]);
|
||||||
|
fmt::print(" -i <input> : input file\n");
|
||||||
|
fmt::print(" -q : quiet mode\n");
|
||||||
|
fmt::print(" -L <output> : output file for Leap\n");
|
||||||
|
fmt::print(" -M <output> : output file for Minato\n");
|
||||||
|
fmt::print(" -X <output> : output file for Xeltek\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// parse arguments
|
||||||
|
for (int i = 1; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "-i") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
inputFile = argv[i + 1];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "-q") == 0) {
|
||||||
|
quiet = true;
|
||||||
|
} else if (strcmp(argv[i], "-L") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
outputFile = argv[i + 1];
|
||||||
|
outputType = PartitionType::kLeap;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "-M") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
outputFile = argv[i + 1];
|
||||||
|
outputType = PartitionType::kMinato;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
} else if (strcmp(argv[i], "-X") == 0) {
|
||||||
|
if (i + 1 < argc) {
|
||||||
|
outputFile = argv[i + 1];
|
||||||
|
outputType = PartitionType::kXeltek;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inputFile.empty()) {
|
||||||
|
fmt::print("Input file is required\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::vector<uint8_t> inputData;
|
||||||
|
if (int rc; !ReadFile(inputFile, inputData, 512, rc)) {
|
||||||
|
fmt::print("Unable to open '{}', rc={}, {}", inputFile, rc, GetErrorMsg(-rc));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::vector<SimplePartitionItem> items;
|
||||||
|
inputType = ReadPartitionTable(inputData, items);
|
||||||
|
if (inputType == PartitionType::kNone) {
|
||||||
|
fmt::print("Invalid or unknown partition table\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (items.empty()) {
|
||||||
|
fmt::print("Empty partition table\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bool hasError = false;
|
||||||
|
{
|
||||||
|
// check if output is valid
|
||||||
|
// 1. range check
|
||||||
|
for (const auto& item: items) {
|
||||||
|
if (item.start >= item.end || item.start + item.size > item.end) {
|
||||||
|
fmt::print("Invalid partition table, start={}, end={}, size={}\n", item.start, item.end, item.size);
|
||||||
|
hasError = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 2. items should be sorted and not overlap
|
||||||
|
uint32_t last = 0;
|
||||||
|
for (const auto& item: items) {
|
||||||
|
if (item.start < last) {
|
||||||
|
fmt::print("Invalid partition table, items are not sorted or overlap\n");
|
||||||
|
hasError = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
last = item.end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hasError) {
|
||||||
|
fmt::print("The partition table has errors, aborting\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// if not quiet, print the partition table
|
||||||
|
if (!quiet) {
|
||||||
|
std::string buf;
|
||||||
|
buf += fmt::format("Input: {} partition(s), {}\n", items.size(), GetPartionTypeLongName(inputType));
|
||||||
|
/**
|
||||||
|
* Index Start End Size
|
||||||
|
* 31 00000000 00000000 00000000
|
||||||
|
*/
|
||||||
|
buf += "Index Start End Size\n";
|
||||||
|
for (size_t i = 0; i < items.size(); i++) {
|
||||||
|
const auto& item = items[i];
|
||||||
|
buf += fmt::format("{:5} {:08x} {:08x} {:08x}\n", i + 1, item.start, item.end, item.size);
|
||||||
|
}
|
||||||
|
fmt::print("{}", buf);
|
||||||
|
}
|
||||||
|
if (outputType != PartitionType::kNone && !outputFile.empty()) {
|
||||||
|
std::vector<uint8_t> outputData = GeneratePartitionTable(items, outputType);
|
||||||
|
if (int rc; !WriteFile(outputFile, outputData, rc)) {
|
||||||
|
fmt::print("Unable to write '{}', rc={}, {}\n", outputFile, rc, GetErrorMsg(-rc));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (!quiet) {
|
||||||
|
fmt::print("Written to '{}', format: {}\n", outputFile, GetPartionTypeLongName(outputType));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The real main entry
|
||||||
|
#if defined(_WIN32) || defined(_WIN64) || 1
|
||||||
|
|
||||||
|
extern "C" void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*);
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
int argcW;
|
||||||
|
wchar_t** argvW;
|
||||||
|
wchar_t** envW;
|
||||||
|
__wgetmainargs(&argcW, &argvW, &envW, 0, nullptr);
|
||||||
|
std::vector<std::string> args;
|
||||||
|
args.reserve(argcW);
|
||||||
|
for (int i = 0; i < argcW; i++) {
|
||||||
|
args.push_back(utfcvt::WideToUTF8(argvW[i]));
|
||||||
|
}
|
||||||
|
std::vector<char*> argvC;
|
||||||
|
argvC.reserve(args.size());
|
||||||
|
for (auto& arg: args) {
|
||||||
|
argvC.push_back(arg.data());
|
||||||
|
}
|
||||||
|
return MainEntry((int) argvC.size(), argvC.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int main(int argc, char** argv) {
|
||||||
|
return MainEntry(argc, argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
149
os_utils.cc
Normal file
149
os_utils.cc
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
//
|
||||||
|
// Created on 2024-05-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "os_utils.h"
|
||||||
|
|
||||||
|
#include "utf_convert.h"
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__)
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
bool ReadFile(std::string_view path, std::vector<uint8_t>& out, size_t maxSize, int& errorCode) {
|
||||||
|
if (maxSize == 0) {
|
||||||
|
errorCode = -EINVAL;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int fd = open(std::string(path).c_str(), O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
errorCode = -errno;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
struct stat64 st{};
|
||||||
|
if (fstat64(fd, &st) < 0) {
|
||||||
|
close(fd);
|
||||||
|
errorCode = -errno;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (st.st_size > maxSize) {
|
||||||
|
close(fd);
|
||||||
|
errorCode = -EOVERFLOW;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.resize(st.st_size);
|
||||||
|
ssize_t remaining = st.st_size;
|
||||||
|
ssize_t read = 0;
|
||||||
|
uint8_t* ptr = out.data();
|
||||||
|
while (remaining > 0) {
|
||||||
|
read = ::read(fd, ptr, remaining);
|
||||||
|
if (read < 0) {
|
||||||
|
close(fd);
|
||||||
|
errorCode = -errno;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remaining -= read;
|
||||||
|
ptr += read;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteFile(std::string_view path, std::span<const uint8_t> data, int& errorCode) {
|
||||||
|
int fd = open(std::string(path).c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
|
if (fd < 0) {
|
||||||
|
errorCode = -errno;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
ssize_t remaining = data.size_bytes();
|
||||||
|
ssize_t written = 0;
|
||||||
|
const uint8_t* ptr = data.data();
|
||||||
|
while (remaining > 0) {
|
||||||
|
written = ::write(fd, ptr, remaining);
|
||||||
|
if (written < 0) {
|
||||||
|
close(fd);
|
||||||
|
errorCode = -errno;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
remaining -= written;
|
||||||
|
ptr += written;
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <cwchar>
|
||||||
|
|
||||||
|
|
||||||
|
std::string GetErrorMsg(int errorCode) {
|
||||||
|
return strerror(std::abs(errorCode));
|
||||||
|
}
|
||||||
|
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
bool ReadFile(std::string_view path, std::vector<uint8_t>& out, size_t maxSize, int& errorCode) {
|
||||||
|
std::wstring wpath = utfcvt::UTF8ToWide(path);
|
||||||
|
HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
|
errorCode = -(int) GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LARGE_INTEGER fileSize;
|
||||||
|
if (!GetFileSizeEx(hFile, &fileSize)) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
errorCode = -(int) GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fileSize.QuadPart > maxSize) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
errorCode = -ERROR_FILE_TOO_LARGE;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
out.resize(fileSize.QuadPart);
|
||||||
|
DWORD bytesRead = 0;
|
||||||
|
if (!ReadFile(hFile, out.data(), fileSize.LowPart, &bytesRead, nullptr)) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
errorCode = -(int) GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteFile(std::string_view path, std::span<const uint8_t> data, int& errorCode) {
|
||||||
|
std::wstring wpath = utfcvt::UTF8ToWide(path);
|
||||||
|
HANDLE hFile = CreateFileW(wpath.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||||
|
if (hFile == INVALID_HANDLE_VALUE) {
|
||||||
|
errorCode = -(int) GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD bytesWritten = 0;
|
||||||
|
if (!WriteFile(hFile, data.data(), (DWORD) data.size_bytes(), &bytesWritten, nullptr)) {
|
||||||
|
CloseHandle(hFile);
|
||||||
|
errorCode = -(int) GetLastError();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CloseHandle(hFile);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetErrorMsg(int errorCode) {
|
||||||
|
LPWSTR msgBuf = nullptr;
|
||||||
|
FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR) &msgBuf, 0, nullptr);
|
||||||
|
std::wstring msg(msgBuf);
|
||||||
|
LocalFree(msgBuf);
|
||||||
|
return utfcvt::WideToUTF8(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "Unsupported platform"
|
||||||
|
#endif
|
||||||
21
os_utils.h
Normal file
21
os_utils.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Created on 2024-05-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef NANDPARTITIONTABLECONVERT_OS_UTILS_H
|
||||||
|
#define NANDPARTITIONTABLECONVERT_OS_UTILS_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <string_view>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
bool ReadFile(std::string_view path, std::vector<uint8_t>& out, size_t maxSize, int& errorCode);
|
||||||
|
|
||||||
|
bool WriteFile(std::string_view path, std::span<const uint8_t> data, int& errorCode);
|
||||||
|
|
||||||
|
std::string GetErrorMsg(int errorCode);
|
||||||
|
|
||||||
|
#endif //NANDPARTITIONTABLECONVERT_OS_UTILS_H
|
||||||
133
utf_convert.cc
Normal file
133
utf_convert.cc
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
//
|
||||||
|
// Created on 2024-05-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "utf_convert.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace utfcvt {
|
||||||
|
|
||||||
|
std::u32string UTF8ToUTF32(std::string_view utf8) {
|
||||||
|
if (utf8.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<int> codePoints;
|
||||||
|
// source may have multiple code points, treat as UTF-8
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < utf8.size()) {
|
||||||
|
if ((utf8[i] & 0b10000000) == 0) {
|
||||||
|
// 1 byte code point, ASCII
|
||||||
|
int c = (utf8[i] & 0b01111111);
|
||||||
|
codePoints.push_back(c);
|
||||||
|
i++;
|
||||||
|
} else if ((utf8[i] & 0b11100000) == 0b11000000) {
|
||||||
|
// 2 byte code point
|
||||||
|
int c = (utf8[i] & 0b00011111) << 6 | (utf8[i + 1] & 0b00111111);
|
||||||
|
codePoints.push_back(c);
|
||||||
|
i += 2;
|
||||||
|
} else if ((utf8[i] & 0b11110000) == 0b11100000) {
|
||||||
|
// 3 byte code point
|
||||||
|
int c = (utf8[i] & 0b00001111) << 12 | (utf8[i + 1] & 0b00111111) << 6 | (utf8[i + 2] & 0b00111111);
|
||||||
|
codePoints.push_back(c);
|
||||||
|
i += 3;
|
||||||
|
} else {
|
||||||
|
// 4 byte code point
|
||||||
|
int c = (utf8[i] & 0b00000111) << 18 | (utf8[i + 1] & 0b00111111) << 12
|
||||||
|
| (utf8[i + 2] & 0b00111111) << 6 | (utf8[i + 3] & 0b00111111);
|
||||||
|
codePoints.push_back(c);
|
||||||
|
i += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {codePoints.begin(), codePoints.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u32string UTF16ToUTF32(std::u16string_view utf16) {
|
||||||
|
if (utf16.empty()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
std::vector<int> codePoints;
|
||||||
|
for (size_t i = 0; i < utf16.size(); ++i) {
|
||||||
|
if (utf16[i] < 0xD800 || utf16[i] > 0xDFFF) {
|
||||||
|
codePoints.push_back(utf16[i]);
|
||||||
|
} else if (i + 1 < utf16.size()) {
|
||||||
|
codePoints.push_back(0x10000 + ((utf16[i] - 0xD800) << 10) + (utf16[i + 1] - 0xDC00));
|
||||||
|
++i;
|
||||||
|
} else {
|
||||||
|
codePoints.push_back(0xFFFD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {codePoints.begin(), codePoints.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string UTF32ToUTF16(std::u32string_view utf32){
|
||||||
|
std::vector<char16_t> utf16;
|
||||||
|
for (auto c: utf32) {
|
||||||
|
if (c <= 0xFFFF) {
|
||||||
|
utf16.push_back(static_cast<char16_t>(c));
|
||||||
|
} else if (c <= 0x10FFFF) {
|
||||||
|
utf16.push_back(static_cast<char16_t>(0xD800 + ((c - 0x10000) >> 10)));
|
||||||
|
utf16.push_back(static_cast<char16_t>(0xDC00 + ((c - 0x10000) & 0x3FF)));
|
||||||
|
} else {
|
||||||
|
utf16.push_back(0xFFFD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {utf16.begin(), utf16.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UTF32ToUTF8(std::u32string_view utf32) {
|
||||||
|
std::string utf8;
|
||||||
|
for (auto c: utf32) {
|
||||||
|
if (c <= 0x7F) {
|
||||||
|
utf8.push_back(static_cast<char>(c));
|
||||||
|
} else if (c <= 0x7FF) {
|
||||||
|
utf8.push_back(static_cast<char>(0xC0 | (c >> 6)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | (c & 0x3F)));
|
||||||
|
} else if (c <= 0xFFFF) {
|
||||||
|
utf8.push_back(static_cast<char>(0xE0 | (c >> 12)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | (c & 0x3F)));
|
||||||
|
} else {
|
||||||
|
utf8.push_back(static_cast<char>(0xF0 | (c >> 18)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | ((c >> 12) & 0x3F)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3F)));
|
||||||
|
utf8.push_back(static_cast<char>(0x80 | (c & 0x3F)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return utf8;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UTF16ToUTF8(std::u16string_view utf16) {
|
||||||
|
return UTF32ToUTF8(UTF16ToUTF32(utf16));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string UTF8ToUTF16(std::string_view utf8) {
|
||||||
|
return UTF32ToUTF16(UTF8ToUTF32(utf8));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
|
||||||
|
// for windows, they are not implemented on linux
|
||||||
|
|
||||||
|
static_assert(sizeof(wchar_t) == sizeof(char16_t));
|
||||||
|
|
||||||
|
std::wstring UTF16ToWide(std::u16string_view utf16) {
|
||||||
|
return {reinterpret_cast<const wchar_t*>(utf16.data()), utf16.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::u16string WideToUTF16(std::wstring_view wide) {
|
||||||
|
return {reinterpret_cast<const char16_t*>(wide.data()), wide.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring UTF8ToWide(std::string_view utf8) {
|
||||||
|
auto utf16 = UTF8ToUTF16(utf8);
|
||||||
|
return UTF16ToWide(utf16);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string WideToUTF8(std::wstring_view wide) {
|
||||||
|
return UTF16ToUTF8(std::u16string_view{reinterpret_cast<const char16_t*>(wide.data()), wide.size()});
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
37
utf_convert.h
Normal file
37
utf_convert.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// Created on 2024-05-17.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef UTF_CONVERT_H
|
||||||
|
#define UTF_CONVERT_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace utfcvt {
|
||||||
|
|
||||||
|
std::string UTF16ToUTF8(std::u16string_view utf16);
|
||||||
|
|
||||||
|
std::u16string UTF8ToUTF16(std::string_view utf8);
|
||||||
|
|
||||||
|
std::u32string UTF8ToUTF32(std::string_view utf8);
|
||||||
|
|
||||||
|
std::string UTF32ToUTF8(std::u32string_view utf32);
|
||||||
|
|
||||||
|
std::u32string UTF16ToUTF32(std::u16string_view utf16);
|
||||||
|
|
||||||
|
std::u16string UTF32ToUTF16(std::u32string_view utf32);
|
||||||
|
|
||||||
|
// for windows, they are not implemented on linux
|
||||||
|
|
||||||
|
std::wstring UTF8ToWide(std::string_view utf8);
|
||||||
|
|
||||||
|
std::string WideToUTF8(std::wstring_view wide);
|
||||||
|
|
||||||
|
std::wstring UTF16ToWide(std::u16string_view utf16);
|
||||||
|
|
||||||
|
std::u16string WideToUTF16(std::wstring_view wide);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //UTF_CONVERT_H
|
||||||
Reference in New Issue
Block a user