[cmake] refactor: Use CPM over submodules #143

Merged
crueter merged 42 commits from refactor/cpm into master 2025-08-04 04:50:17 +02:00
20 changed files with 2 additions and 757 deletions
Showing only changes of commit d18abd3013 - Show all commits

3
.gitmodules vendored
View file

@ -7,6 +7,3 @@
[submodule "externals/boost-headers"]
path = externals/boost-headers
url = https://github.com/boostorg/headers.git
[submodule "externals/nx_tzdb/tzdb_to_nx/externals/tz/tz"]
path = externals/nx_tzdb/tzdb_to_nx/externals/tz/tz
url = https://github.com/eggert/tz.git

View file

@ -117,7 +117,7 @@ endif()
option(YUZU_ENABLE_LTO "Enable link-time optimization" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" OFF)
option(YUZU_DOWNLOAD_TIME_ZONE_DATA "Always download time zone binaries" ON)
option(YUZU_ENABLE_PORTABLE "Allow yuzu to enable portable mode if a user folder is found in the CWD" ON)

View file

@ -32,6 +32,7 @@ set(NX_TZDB_ARCHIVE "${CMAKE_CURRENT_BINARY_DIR}/${NX_TZDB_VERSION}.zip")
set(NX_TZDB_ROMFS_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx_tzdb")
# TODO(crueter): figure out tz download/etc
if ((NOT CAN_BUILD_NX_TZDB OR YUZU_DOWNLOAD_TIME_ZONE_DATA) AND NOT EXISTS ${NX_TZDB_ROMFS_DIR})
set(NX_TZDB_DOWNLOAD_URL "https://github.com/lat9nq/tzdb_to_nx/releases/download/${NX_TZDB_VERSION}/${NX_TZDB_VERSION}.zip")

View file

@ -1,52 +0,0 @@
name: CMake
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
strategy:
matrix:
platform: [ubuntu-latest,macos-latest]
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v3
with:
submodules: 'true'
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: |
cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
echo "nx_tzdb_dir=$(grep NX_TZDB_DIR ${{github.workspace}}/build/CMakeCache.txt | sed 's/.*=//g')" > "$GITHUB_ENV"
- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --target x80e
- name: Package
run: |
mkdir -p ${{github.workspace}}/artifacts
cp -Rv ${{ env.nx_tzdb_dir }} ${{github.workspace}}/artifacts/
- name: Version
run: |
echo "nx_version=$(cat ${{ env.nx_tzdb_dir }}/version.txt)" > "$GITHUB_ENV"
- name: Upload
uses: actions/upload-artifact@v3
with:
name: ${{ env.nx_version }}_${{ matrix.platform }}
path: artifacts/nx

View file

@ -1,2 +0,0 @@
.cache
build

View file

@ -1,3 +0,0 @@
[submodule "externals/tz/tz"]
path = externals/tz/tz
url = https://github.com/eggert/tz.git

View file

@ -1,19 +0,0 @@
cmake_minimum_required(VERSION 3.10)
project(tzdb2nx VERSION 1.0)
option(TZDB2NX_ZONEINFO_DIR "Specify a custom zoneinfo directory containing time zone data you wish to use" "")
option(TZDB2NX_VERSION "Specify a custom zoneinfo version with the directory" "")
if (TZDB2NX_ZONEINFO_DIR AND NOT TZDB2NX_VERSION)
message(FATAL_ERROR "TZDB2NX_ZONEINFO_DIR was specified but TZDB2NX_VERSION was left undefined.")
endif()
set(CMAKE_CXX_STANDARD 20)
if (APPLE)
find_package(Intl REQUIRED)
endif()
add_subdirectory(externals)
add_subdirectory(src)

View file

@ -1,10 +0,0 @@
set(WITH_DIRECTORIES ${CMAKE_ARGV3})
set(RECURSE ${CMAKE_ARGV4})
set(HOW_TO_GLOB "GLOB")
if (RECURSE)
set(HOW_TO_GLOB "GLOB_RECURSE")
endif()
file(${HOW_TO_GLOB} FILE_LIST LIST_DIRECTORIES ${WITH_DIRECTORIES} RELATIVE ${CMAKE_SOURCE_DIR} "*")
execute_process(COMMAND ${CMAKE_COMMAND} -E echo "${FILE_LIST};")

View file

@ -1,21 +0,0 @@
The MIT License (MIT)
Copyright © 2023 lat9nq
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the “Software”), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,22 +0,0 @@
# tzdb_to_nx
This is a CMake/C++ project to convert RFC 8536 time zone data to the Nintendo Switch's format.
This makes use a lot of Unix system calls as well as a bash script to convert the data, so it likely requires a bit of work to port to a non-POSIX platform, such as Windows.
Intended for use with the [yuzu Emulator](https://yuzu-emu.org/) project, but the project in the future likely won't ship synthesized Switch archives.
That leaves this project in a place where it is not likely to be used, but will remain here as a reference.
- tzdb: CMake and bash script to build and convert time zone data from https://www.iana.org/time-zones into the Nintendo Switch's format.
- tzdb2nx: C++ program that converts a single tzif file to the Nintendo's format.
The fine folks over at [SwitchBrew](https://switchbrew.org/wiki/PSC_services#ITimeZoneService) have left very helpful information on reading the data.
Nintendo's file is simply the TZif version 2 data, with standard_indicators and ut_indicators data stripped out (and the necessary modifications needed in the header to make the data valid).
This means the TZif 1 data is not present, so essentially we are left with the second half of each file.
Nintendo also does not seem to run the `zic` program on their output when they build the time zone data.
I have left the relevant build command for that in src/tzdb/CMakeLists.txt commented out, but it isn't used here.
This lets the project produce data identical to Nintendo's firmware for time zones, however this code does not produce the time zone data on US/Pacific-New or America/East-Saskatchewan (I may have bunged up the actual paths for these as this is 3 day old memory).
The CMake and C++ code in this repository is licensed under the MIT License.
The source files date.c, newstrftime.3 and strftime.c from submodule eggert/tz use the BSD-3 clause license [[source]](https://github.com/eggert/tz/blob/main/LICENSE).
The time zone data output from this repository, like those found in archives in the Release setcion, is in the public domain [[source]](https://github.com/eggert/tz/blob/main/LICENSE).

View file

@ -1 +0,0 @@
add_subdirectory(tz)

View file

@ -1,78 +0,0 @@
set(TZ_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tz" CACHE PATH "Time zone source directory")
set(TZ_DIR "${CMAKE_CURRENT_BINARY_DIR}/tz")
set(TZ_TMP_SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/tmpsrc")
set(TZIF_LIST_FILE "${CMAKE_CURRENT_BINARY_DIR}/tzif_list.txt" CACHE PATH "List of zone info files")
if (TZDB2NX_ZONEINFO_DIR)
set(TZ_ZONEINFO_DIR "${TZDB2NX_ZONEINFO_DIR}" CACHE PATH "Time zone info data directory")
else()
set(TZ_ZONEINFO_DIR "${TZ_DIR}/usr/share/zoneinfo" CACHE PATH "Time zone info data directory")
endif()
find_program(GNU_MAKE make)
if (NOT GNU_MAKE)
message(FATAL_ERROR "GNU make not found")
endif()
find_program(GIT_PROGRAM git)
if (NOT GIT_PROGRAM)
message(FATAL_ERROR "git program not found")
endif()
if (NOT EXISTS "${TZ_DIR}" OR NOT EXISTS "${TZIF_LIST_FILE}")
if (NOT TZDB2NX_ZONEINFO_DIR) # If a custom zoneinfo directory was specified
# tz's makefile can only build in-tree, so copy the whole source tree to a
# separate directory before building.
execute_process(
COMMAND
${GIT_PROGRAM} clone --depth=1 "file://${TZ_SOURCE_DIR}" "${TZ_TMP_SOURCE_DIR}"
# No need to be fatal, on SunOS this works fine - COMMAND_ERROR_IS_FATAL ANY
)
if (APPLE)
set(TZ_MAKEFLAGS "LDLIBS=${Intl_LIBRARY}")
else()
set(TZ_MAKEFLAGS)
endif()
execute_process(
COMMAND
${GNU_MAKE} DESTDIR=${TZ_DIR} ${TZ_MAKEFLAGS} install
WORKING_DIRECTORY
${TZ_TMP_SOURCE_DIR}
COMMAND_ERROR_IS_FATAL ANY
)
unset(TZ_MAKEFLAGS)
# Step taken by Arch Linux packaging, but Nintendo apparently skips it
# execute_process(
# COMMAND
# "${TZDB_LOCATION}/zic" -b fat -d ${TZDB_ZONEINFO} africa antarctica asia australasia europe northamerica southamerica etcetera backward factory
# WORKING_DIRECTORY
# "${TZDB_LOCATION}"
# COMMAND_ERROR_IS_FATAL ANY
# )
endif()
execute_process(
COMMAND
${CMAKE_COMMAND} -P ${PROJECT_SOURCE_DIR}/CMakeModules/list_directory.cmake false ON
WORKING_DIRECTORY
"${TZ_ZONEINFO_DIR}"
OUTPUT_VARIABLE
TZIF_SCAN
)
set(TZIF_LIST "")
foreach(CANDIDATE ${TZIF_SCAN})
if (CANDIDATE STREQUAL "\n")
continue()
endif()
set(TZIF_FILE "${TZ_ZONEINFO_DIR}/${CANDIDATE}")
file(READ "${TZIF_FILE}" HEADER LIMIT 4)
string(SUBSTRING "${HEADER}" 0 4 HEADER) # Remove trailing newline
if (HEADER STREQUAL "TZif")
file(APPEND "${TZIF_LIST_FILE}" "${TZIF_FILE}\n")
endif()
endforeach()
endif()

@ -1 +0,0 @@
Subproject commit 16ce126a87c5f130cde8b8dce73b38952a19f085

View file

@ -1,11 +0,0 @@
add_compile_options(
-Werror=all
-Werror=extra
-Werror=shadow
)
include_directories(.)
add_subdirectory(tzdb2nx)
add_subdirectory(tzdb)

View file

@ -1,90 +0,0 @@
find_program(GIT_PROGRAM git)
if (NOT GIT_PROGRAM)
message(FATAL_ERROR "git program not found")
endif()
find_program(GNU_DATE date)
if (NOT GNU_DATE)
message(FATAL_ERROR "date program not found")
endif()
set(NX_TZDB_DIR "${CMAKE_CURRENT_BINARY_DIR}/nx" CACHE PATH "Path to Switch-style time zone data")
set(NX_ZONEINFO_DIR "${NX_TZDB_DIR}/zoneinfo")
set(TZDB_VERSION_FILE ${TZ_SOURCE_DIR}/NEWS)
if (NOT "${TZDB2NX_VERSION}" STREQUAL "")
set(TZDB_VERSION "${TZDB2NX_VERSION}\n")
else()
execute_process(
COMMAND
${GIT_PROGRAM} log --pretty=%at -n1 NEWS
OUTPUT_VARIABLE
TZ_COMMIT_TIME
WORKING_DIRECTORY
${TZ_SOURCE_DIR}
COMMAND_ERROR_IS_FATAL ANY)
string(REPLACE "\n" "" TZ_COMMIT_TIME "${TZ_COMMIT_TIME}")
if (APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD")
set(VERSION_COMMAND ${GNU_DATE} -r ${TZ_COMMIT_TIME} +%y%m%d)
else ()
set(VERSION_COMMAND ${GNU_DATE} +%y%m%d --date=@${TZ_COMMIT_TIME})
endif ()
execute_process(
COMMAND
${VERSION_COMMAND}
OUTPUT_VARIABLE
TZDB_VERSION
COMMAND_ERROR_IS_FATAL ANY)
endif()
set(NX_VERSION_FILE ${NX_TZDB_DIR}/version.txt)
file(WRITE ${NX_VERSION_FILE} "${TZDB_VERSION}")
add_custom_target(x80e
DEPENDS
tzdb2nx
${NX_VERSION_FILE})
set(BINARY_LIST_TXT ${NX_TZDB_DIR}/binaryList.txt)
add_custom_command(
OUTPUT
${BINARY_LIST_TXT}
COMMAND
${CMAKE_COMMAND} -P ${CMAKE_CURRENT_SOURCE_DIR}/generate_binary_list_txt.cmake ${BINARY_LIST_TXT} ${PROJECT_SOURCE_DIR}/CMakeModules/list_directory.cmake
WORKING_DIRECTORY
${NX_ZONEINFO_DIR})
add_custom_target(time_zone_binary_list
DEPENDS ${BINARY_LIST_TXT})
add_dependencies(x80e time_zone_binary_list)
set(TZ_DATA_LIST "")
file(STRINGS "${TZIF_LIST_FILE}" TZ_FILES)
foreach(FILE ${TZ_FILES})
file(RELATIVE_PATH TARG "${TZ_ZONEINFO_DIR}" "${FILE}")
get_filename_component(TARG_PATH "${NX_ZONEINFO_DIR}/${TARG}" DIRECTORY)
string(REGEX REPLACE "\/" "_" TARG_SANITIZED "${TARG}")
set(NX_TZ_TARGET ${NX_ZONEINFO_DIR}/${TARG})
add_custom_command(
OUTPUT
${NX_TZ_TARGET}
COMMAND
mkdir -p ${TARG_PATH}
COMMAND
${TZDB2NX_PATH} ${FILE} ${NX_ZONEINFO_DIR}/${TARG}
DEPENDS
tzdb2nx)
list(APPEND TZ_DATA_LIST ${NX_TZ_TARGET})
endforeach()
add_custom_target(time_zone_data
DEPENDS ${TZ_DATA_LIST})
add_dependencies(x80e time_zone_data)
add_dependencies(time_zone_binary_list time_zone_data)

View file

@ -1,52 +0,0 @@
set(BINARY_LIST_TXT ${CMAKE_ARGV3})
set(LIST_DIR_CMAKE ${CMAKE_ARGV4})
# Fill text file with zone names
# Issue: Hyphens/underscores are not handled the same way Nintendo handles them
function(get_files_nx TARG SUB_DIR)
execute_process(
COMMAND
${CMAKE_COMMAND} -P ${LIST_DIR_CMAKE} false OFF
WORKING_DIRECTORY
${TARG}
OUTPUT_VARIABLE
FILE_LIST
)
list(SORT FILE_LIST)
execute_process(
COMMAND
${CMAKE_COMMAND} -P ${LIST_DIR_CMAKE} true OFF
WORKING_DIRECTORY
${TARG}
OUTPUT_VARIABLE
DIR_LIST
)
foreach(FILE ${FILE_LIST})
if(FILE STREQUAL "\n")
continue()
endif()
list(REMOVE_ITEM DIR_LIST FILE)
if (SUB_DIR)
file(APPEND ${BINARY_LIST_TXT} "${SUB_DIR}/${FILE}\r\n")
else()
file(APPEND ${BINARY_LIST_TXT} "${FILE}\r\n")
endif()
endforeach()
list(SORT DIR_LIST)
foreach(DIR ${DIR_LIST})
if (NOT DIR OR DIR STREQUAL "\n")
continue()
endif()
if (SUB_DIR)
get_files_nx(${TARG}/${DIR} ${SUB_DIR}/${DIR})
else()
get_files_nx(${TARG}/${DIR} ${DIR})
endif()
endforeach()
endfunction()
get_files_nx(${CMAKE_SOURCE_DIR} "")

View file

@ -1,6 +0,0 @@
add_executable(tzdb2nx
main.cpp
tzif.cpp
tzif.h)
set(TZDB2NX_PATH "$<TARGET_FILE:tzdb2nx>" CACHE PATH "Path to tzdb2nx path")

View file

@ -1,161 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "tzif.h"
#include <array>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <fcntl.h>
#include <getopt.h>
#include <poll.h>
#include <sys/stat.h>
#include <cstdint>
#include <unistd.h>
constexpr std::size_t ten_megabytes{(1 << 20) * 10};
static void ShortHelp(const char *argv0) {
std::fprintf(stderr, "Usage: %s [INFILE] [OUTFILE]\n", argv0);
}
static void PrintArg(const char *short_arg, const char *long_arg,
const char *text) {
std::fprintf(stderr, "%5s, %-20s %s\n", short_arg, long_arg, text);
}
static void PrintHelp(const char *argv0) {
ShortHelp(argv0);
std::fprintf(stderr,
"Converts a TZif file INFILE from the RFC8536 format to a "
"Nintendo Switch compatible file OUTFILE.\nWith no arguments, "
"tzdb2nx can read and write from stdin/stdout, "
"respectively.\nGiving no arguments without input will print "
"usage information and exit the program.\n\nArguments:\n");
PrintArg("-h", "--help", "Print this help text and exit");
}
int main(int argc, char *argv[]) {
int f{STDIN_FILENO};
const char *filename{"(stdin)"};
std::size_t filesize{ten_megabytes};
const char *optstring = "h";
int c;
const struct option longopts[] = {
{
"help",
no_argument,
nullptr,
'h',
},
{
nullptr,
0,
nullptr,
0,
},
};
while ((c = getopt_long(argc, argv, optstring, longopts, nullptr)) != -1) {
switch (c) {
case 'h':
PrintHelp(argv[0]);
return -1;
case '?':
ShortHelp(argv[0]);
return -1;
}
}
if (argc > 1) {
filename = argv[1];
f = open(filename, O_RDONLY);
if (f == -1) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
struct stat statbuf;
fstat(f, &statbuf);
filesize = statbuf.st_size;
} else {
struct pollfd fds {
f, POLLIN, 0,
};
const int result = poll(&fds, 1, 0);
if (result == 0) {
std::fprintf(stderr, "%s: No input\n", filename);
ShortHelp(argv[0]);
return -1;
}
}
std::uint8_t *buf = new std::uint8_t[filesize];
filesize = read(f, buf, filesize);
if (filesize == static_cast<std::size_t>(-1)) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
int result = close(f);
if (result == -1) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
if (filesize < 4) {
std::fprintf(stderr, "%s: Too small\n", filename);
return -1;
}
if (std::strncmp(reinterpret_cast<const char *>(buf), "TZif", 4) != 0) {
std::fprintf(stderr, "%s: Bad magic number\n", filename);
return -1;
}
const std::unique_ptr<Tzif::Data> tzif_data = Tzif::ReadData(buf, filesize);
if (tzif_data == nullptr) {
std::fprintf(stderr, "%s: Error occured while reading data\n", filename);
return -1;
}
delete[] buf;
std::vector<std::uint8_t> output_buffer;
tzif_data->ReformatNintendo(output_buffer);
filename = "(stdout)";
f = STDOUT_FILENO;
if (argc > 2) {
filename = argv[2];
f = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0664);
if (f == -1) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
}
result = write(f, output_buffer.data(), output_buffer.size());
if (result == -1) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
result = close(f);
if (result == -1) {
const int err = errno;
std::fprintf(stderr, "%s: %s\n", filename, std::strerror(err));
return err;
}
return 0;
}

View file

@ -1,149 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "tzif.h"
#include <cstdint>
#include <cstring>
#include <memory>
#include <cstdint>
namespace Tzif {
static std::size_t SkipToVersion2(const std::uint8_t *data, std::size_t size) {
char magic[5];
const std::uint8_t *p{data};
std::memcpy(magic, data, 4);
magic[4] = '\0';
if (std::strcmp(magic, "TZif") != 0) {
return -1;
}
do {
p++;
if (p >= data + size) {
return -1;
}
} while (std::strncmp(reinterpret_cast<const char *>(p), "TZif", 4) != 0);
return p - data;
}
template <typename Type> constexpr static void SwapEndianess(Type *value) {
std::uint8_t *data = reinterpret_cast<std::uint8_t *>(value);
union {
std::uint8_t data[sizeof(Type)];
Type value;
} temp;
for (std::uint32_t i = 0; i < sizeof(Type); i++) {
std::uint32_t alt_index = sizeof(Type) - i - 1;
temp.data[alt_index] = data[i];
}
*value = temp.value;
}
static void FlipHeader(Header &header) {
SwapEndianess(&header.isutcnt);
SwapEndianess(&header.isstdcnt);
SwapEndianess(&header.leapcnt);
SwapEndianess(&header.timecnt);
SwapEndianess(&header.typecnt);
SwapEndianess(&header.charcnt);
}
std::unique_ptr<DataImpl> ReadData(const std::uint8_t *data, std::size_t size) {
const std::size_t v2_offset = SkipToVersion2(data, size);
if (v2_offset == static_cast<std::size_t>(-1)) {
return nullptr;
}
const std::uint8_t *p = data + v2_offset;
Header header;
std::memcpy(&header, p, sizeof(header));
p += sizeof(header);
FlipHeader(header);
const std::size_t data_block_length =
header.timecnt * sizeof(int64_t) + header.timecnt * sizeof(std::uint8_t) +
header.typecnt * sizeof(TimeTypeRecord) +
header.charcnt * sizeof(int8_t) + header.isstdcnt * sizeof(std::uint8_t) +
header.isutcnt * sizeof(std::uint8_t);
if (v2_offset + data_block_length + sizeof(Header) > size) {
return nullptr;
}
std::unique_ptr<DataImpl> impl = std::make_unique<DataImpl>();
impl->header = header;
const auto copy =
[]<typename Type>(std::unique_ptr<Type[]> &array, int length,
const std::uint8_t *const &ptr) -> const std::uint8_t * {
const std::size_t region_length = length * sizeof(Type);
array = std::make_unique<Type[]>(length);
std::memcpy(array.get(), ptr, region_length);
return ptr + region_length;
};
p = copy(impl->transition_times, header.timecnt, p);
p = copy(impl->transition_types, header.timecnt, p);
p = copy(impl->local_time_type_records, header.typecnt, p);
p = copy(impl->time_zone_designations, header.charcnt, p);
p = copy(impl->standard_indicators, header.isstdcnt, p);
p = copy(impl->ut_indicators, header.isutcnt, p);
const std::size_t footer_string_length = data + size - p - 2;
p++;
if (p + footer_string_length > data + size ||
p + footer_string_length < data) {
return nullptr;
}
impl->footer.tz_string = std::make_unique<char[]>(footer_string_length);
std::memcpy(impl->footer.tz_string.get(), p, footer_string_length);
impl->footer.footer_string_length = footer_string_length;
return impl;
}
static void PushToBuffer(std::vector<std::uint8_t> &buffer, const void *data,
std::size_t size) {
const std::uint8_t *p{reinterpret_cast<const std::uint8_t *>(data)};
for (std::size_t i = 0; i < size; i++) {
buffer.push_back(*p);
p++;
}
}
void DataImpl::ReformatNintendo(std::vector<std::uint8_t> &buffer) const {
buffer.clear();
Header header_copy{header};
header_copy.isstdcnt = 0;
header_copy.isutcnt = 0;
FlipHeader(header_copy);
PushToBuffer(buffer, &header_copy, sizeof(Header));
PushToBuffer(buffer, transition_times.get(),
header.timecnt * sizeof(int64_t));
PushToBuffer(buffer, transition_types.get(),
header.timecnt * sizeof(std::uint8_t));
PushToBuffer(buffer, local_time_type_records.get(),
header.typecnt * sizeof(TimeTypeRecord));
PushToBuffer(buffer, time_zone_designations.get(),
header.charcnt * sizeof(int8_t));
// omit standard_indicators
// omit ut_indicators
PushToBuffer(buffer, &footer.nl_a, 1);
PushToBuffer(buffer, footer.tz_string.get(), footer.footer_string_length);
PushToBuffer(buffer, &footer.nl_b, 1);
}
} // namespace Tzif

View file

@ -1,75 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <array>
#include <memory>
#include <cstdint>
#include <vector>
namespace Tzif {
typedef struct {
char magic[4];
std::uint8_t version;
std::uint8_t reserved[15];
std::uint32_t isutcnt;
std::uint32_t isstdcnt;
std::uint32_t leapcnt;
std::uint32_t timecnt;
std::uint32_t typecnt;
std::uint32_t charcnt;
} Header;
static_assert(sizeof(Header) == 0x2c);
class Footer {
public:
explicit Footer() = default;
~Footer() = default;
const char nl_a{'\n'};
std::unique_ptr<char[]> tz_string;
const char nl_b{'\n'};
std::size_t footer_string_length;
};
#pragma pack(push, 1)
typedef struct {
std::uint32_t utoff;
std::uint8_t dst;
std::uint8_t idx;
} TimeTypeRecord;
#pragma pack(pop)
static_assert(sizeof(TimeTypeRecord) == 0x6);
class Data {
public:
explicit Data() = default;
virtual ~Data() = default;
virtual void ReformatNintendo(std::vector<std::uint8_t> &buffer) const = 0;
};
class DataImpl : public Data {
public:
explicit DataImpl() = default;
~DataImpl() override = default;
void ReformatNintendo(std::vector<std::uint8_t> &buffer) const override;
Header header;
Footer footer;
std::unique_ptr<int64_t[]> transition_times;
std::unique_ptr<std::uint8_t[]> transition_types;
std::unique_ptr<TimeTypeRecord[]> local_time_type_records;
std::unique_ptr<int8_t[]> time_zone_designations;
std::unique_ptr<std::uint8_t[]> standard_indicators;
std::unique_ptr<std::uint8_t[]> ut_indicators;
};
std::unique_ptr<DataImpl> ReadData(const std::uint8_t *data, std::size_t size);
} // namespace Tzif