/* * This file is part of Luma3DS * Copyright (C) 2016-2020 Aurora Wright, TuxSH * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * * Additional Terms 7.b and 7.c of GPLv3 apply to this file: * * Requiring preservation of specified reasonable legal notices or * author attributions in that material or in the Appropriate Legal * Notices displayed by works containing it. * * Prohibiting misrepresentation of the origin of that material, * or requiring that modified versions of such material be marked in * reasonable ways as different from the original version. */ #include <3ds.h> #include #include #include "utils.h" #include "minisoc.h" #define NUM2BCD(n) ((n<99) ? (((n/10)*0x10)|(n%10)) : 0x99) #define NTP_TIMESTAMP_DELTA 2208988800ull #define MAKE_IPV4(a,b,c,d) ((a) << 24 | (b) << 16 | (c) << 8 | (d)) #ifndef NTP_IP #define NTP_IP MAKE_IPV4(51, 137, 137, 111) // time.windows.com #endif typedef struct RtcTime { // From 3dbrew u8 seconds; u8 minutes; u8 hours; u8 dayofweek; u8 dayofmonth; u8 month; u8 year; u8 leapcount; } RtcTime; // From https://github.com/lettier/ntpclient/blob/master/source/c/main.c typedef struct NtpPacket { u8 li_vn_mode; // Eight bits. li, vn, and mode. // li. Two bits. Leap indicator. // vn. Three bits. Version number of the protocol. // mode. Three bits. Client will pick mode 3 for client. u8 stratum; // Eight bits. Stratum level of the local clock. u8 poll; // Eight bits. Maximum interval between successive messages. u8 precision; // Eight bits. Precision of the local clock. u32 rootDelay; // 32 bits. Total round trip delay time. u32 rootDispersion; // 32 bits. Max error aloud from primary clock source. u32 refId; // 32 bits. Reference clock identifier. u32 refTm_s; // 32 bits. Reference time-stamp seconds. u32 refTm_f; // 32 bits. Reference time-stamp fraction of a second. u32 origTm_s; // 32 bits. Originate time-stamp seconds. u32 origTm_f; // 32 bits. Originate time-stamp fraction of a second. u32 rxTm_s; // 32 bits. Received time-stamp seconds. u32 rxTm_f; // 32 bits. Received time-stamp fraction of a second. u32 txTm_s; // 32 bits and the most important field the client cares about. Transmit time-stamp seconds. u32 txTm_f; // 32 bits. Transmit time-stamp fraction of a second. } NtpPacket; // Total: 384 bits or 48 bytes. void rtcToBcd(u8 *out, const RtcTime *in) { memcpy(out, in, 8); for (u32 i = 0; i < 8; i++) { u8 units = out[i] % 10; u8 tens = (out[i] - units) / 10; out[i] = (tens << 4) | units; } } Result ntpGetTimeStamp(time_t *outTimestamp) { Result res = 0; struct linger linger; res = miniSocInit(); if(R_FAILED(res)) return res; int sock = socSocket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in servAddr = {0}; // Server address data structure. NtpPacket packet = {0}; // Set the first byte's bits to 00,011,011 for li = 0, vn = 3, and mode = 3. The rest will be left set to zero. packet.li_vn_mode = 0x1b; // Zero out the server address structure. servAddr.sin_family = AF_INET; // Copy the server's IP address to the server address structure. servAddr.sin_addr.s_addr = htonl(NTP_IP); // Convert the port number integer to network big-endian style and save it to the server address structure. servAddr.sin_port = htons(123); // Call up the server using its IP address and port number. res = -1; if(socConnect(sock, (struct sockaddr *)&servAddr, sizeof(struct sockaddr_in)) < 0) goto cleanup; if(socSend(sock, &packet, sizeof(NtpPacket), 0) < 0) goto cleanup; if(socRecv(sock, &packet, sizeof(NtpPacket), 0) < 0) goto cleanup; res = 0; // These two fields contain the time-stamp seconds as the packet left the NTP server. // The number of seconds correspond to the seconds passed since 1900. // ntohl() converts the bit/byte order from the network's to host's "endianness". packet.txTm_s = ntohl(packet.txTm_s); // Time-stamp seconds. packet.txTm_f = ntohl(packet.txTm_f); // Time-stamp fraction of a second. // Extract the 32 bits that represent the time-stamp seconds (since NTP epoch) from when the packet left the server. // Subtract 70 years worth of seconds from the seconds since 1900. // This leaves the seconds since the UNIX epoch of 1970. // (1900)------------------(1970)**************************************(Time Packet Left the Server) *outTimestamp = (time_t)(packet.txTm_s - NTP_TIMESTAMP_DELTA); cleanup: linger.l_onoff = 1; linger.l_linger = 0; socSetsockopt(sock, SOL_SOCKET, SO_LINGER, &linger, sizeof(struct linger)); socClose(sock); miniSocExit(); return res; } Result ntpSetTimeDate(const struct tm *localt) { Result res = mcuHwcInit(); if (R_FAILED(res)) return res; res = cfguInit(); if (R_FAILED(res)) goto cleanup; // First, set the config RTC offset to 0 u8 rtcOff = 0; u8 rtcOff2[8] = {0}; res = CFG_SetConfigInfoBlk4(1, 0x10000, &rtcOff); if (R_FAILED(res)) goto cleanup; res = CFG_SetConfigInfoBlk4(8, 0x30001, rtcOff2); if (R_FAILED(res)) goto cleanup; u8 yr = (u8)(localt->tm_year - 100); // Update the RTC u8 bcd[8]; RtcTime lt = { .seconds = (u8)localt->tm_sec, .minutes = (u8)localt->tm_min, .hours = (u8)localt->tm_hour, .dayofweek = (u8)localt->tm_wday, .dayofmonth = (u8)localt->tm_mday, .month = (u8)(localt->tm_mon + 1), .year = yr, .leapcount = 0, }; rtcToBcd(bcd, <); res = MCUHWC_WriteRegister(0x30, bcd, 7); if (R_FAILED(res)) goto cleanup; // Save the config changes res = CFG_UpdateConfigSavegame(); cleanup: mcuHwcExit(); cfguExit(); return res; }