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