// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later #include "tzif.h" #include #include #include #include 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(p), "TZif", 4) != 0); return p - data; } template constexpr static void SwapEndianess(Type *value) { std::uint8_t *data = reinterpret_cast(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 ReadData(const std::uint8_t *data, std::size_t size) { const std::size_t v2_offset = SkipToVersion2(data, size); if (v2_offset == static_cast(-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 impl = std::make_unique(); impl->header = header; const auto copy = [](std::unique_ptr &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(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(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 &buffer, const void *data, std::size_t size) { const std::uint8_t *p{reinterpret_cast(data)}; for (std::size_t i = 0; i < size; i++) { buffer.push_back(*p); p++; } } void DataImpl::ReformatNintendo(std::vector &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