3DS-RPC/api/love.py
2024-09-06 22:32:59 -05:00

64 lines
2.1 KiB
Python

# Created by Deltaion Lee (MCMi460) on Github
# Convert `friendCode` into `principalId`
# This method took two weeks to figure out and five minutes to code
# Thank you, https://www.3dbrew.org/wiki/Principal_ID
# for essentially making my efforts have worth
# I would kiss Meleemeister (https://www.3dbrew.org/wiki/Special:Contributions/Meleemeister)
from . import *
import hashlib
class FriendCodeValidityError(Exception):
pass
class PrincipalIDValidityError(Exception):
pass
# Structure of a friendCode:
#
# (0x__ << 32) | 0x________
# Checksum PrincipalID
#
def friend_code_to_principal_id(friend_code: str) -> int:
# Ensure this friend code has no hyphens, just its 12 digits.
friend_code = ''.join(filter(str.isdigit, str(friend_code))).zfill(12)
if len(friend_code) != 12:
raise FriendCodeValidityError('an invalid friend code was passed')
# Convert friend_code into hexadecimal
checksum_principal = ('%08X' % int(friend_code)).zfill(10)
# Remove most significant byte and convert to integer
principal_id = int(checksum_principal[2:], 16)
# Separate checksum from checksum_principal
checksum_byte = hex(int(checksum_principal[:2], 16))
if not check_principal_id_validity(checksum_byte, principal_id):
raise FriendCodeValidityError('an invalid friend code was passed')
return principal_id
def check_principal_id_validity(checksum_byte: int, principal_id: int) -> bool:
return generate_checksum_byte(principal_id) == checksum_byte
# https://www.3dbrew.org/wiki/FRDU:PrincipalIdToFrien
def generate_checksum_byte(principal_id: int) -> str:
# Little-endian SHA-1 of principal_id
sha1 = hashlib.sha1(principal_id.to_bytes(4, byteorder='little'))
# Shift first byte to the right by one
return hex(int(sha1.hexdigest()[:2], 16) >> 1)
def principal_id_to_friend_code(principal_id: int) -> int:
if not isinstance(principal_id, int):
raise PrincipalIDValidityError('an invalid principal id was passed')
checksum_byte = generate_checksum_byte(principal_id)
friend_code = checksum_byte + hex(principal_id)[2:].zfill(8)
return int(friend_code.replace('0x', ''), 16)