No known key found for this signature in database
GPG Key ID: 425ACD2D4830EBC6
20 changed files with 2 additions and 757 deletions
-
3.gitmodules
-
2CMakeLists.txt
-
1externals/nx_tzdb/CMakeLists.txt
-
52externals/nx_tzdb/tzdb_to_nx/.github/workflows/cmake.yml
-
2externals/nx_tzdb/tzdb_to_nx/.gitignore
-
3externals/nx_tzdb/tzdb_to_nx/.gitmodules
-
19externals/nx_tzdb/tzdb_to_nx/CMakeLists.txt
-
10externals/nx_tzdb/tzdb_to_nx/CMakeModules/list_directory.cmake
-
21externals/nx_tzdb/tzdb_to_nx/LICENSE
-
22externals/nx_tzdb/tzdb_to_nx/README.md
-
1externals/nx_tzdb/tzdb_to_nx/externals/CMakeLists.txt
-
78externals/nx_tzdb/tzdb_to_nx/externals/tz/CMakeLists.txt
-
1externals/nx_tzdb/tzdb_to_nx/externals/tz/tz
-
11externals/nx_tzdb/tzdb_to_nx/src/CMakeLists.txt
-
90externals/nx_tzdb/tzdb_to_nx/src/tzdb/CMakeLists.txt
-
52externals/nx_tzdb/tzdb_to_nx/src/tzdb/generate_binary_list_txt.cmake
-
6externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/CMakeLists.txt
-
161externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/main.cpp
-
149externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.cpp
-
75externals/nx_tzdb/tzdb_to_nx/src/tzdb2nx/tzif.h
@ -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 |
|
||||
@ -1,2 +0,0 @@ |
|||||
.cache |
|
||||
build |
|
||||
@ -1,3 +0,0 @@ |
|||||
[submodule "externals/tz/tz"] |
|
||||
path = externals/tz/tz |
|
||||
url = https://github.com/eggert/tz.git |
|
||||
@ -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) |
|
||||
@ -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};") |
|
||||
@ -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. |
|
||||
@ -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). |
|
||||
@ -1 +0,0 @@ |
|||||
add_subdirectory(tz) |
|
||||
@ -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 |
|
||||
@ -1,11 +0,0 @@ |
|||||
add_compile_options( |
|
||||
-Werror=all |
|
||||
-Werror=extra |
|
||||
|
|
||||
-Werror=shadow |
|
||||
) |
|
||||
|
|
||||
include_directories(.) |
|
||||
|
|
||||
add_subdirectory(tzdb2nx) |
|
||||
add_subdirectory(tzdb) |
|
||||
@ -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) |
|
||||
@ -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} "") |
|
||||
|
|
||||
@ -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") |
|
||||
@ -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; |
|
||||
} |
|
||||
@ -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
|
|
||||
@ -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 |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue