1
0
mirror of synced 2024-11-23 22:10:59 +01:00

Additional typing fixes for safety and to be able to compile more code with mypyc.

This commit is contained in:
Jennifer Taylor 2021-05-31 18:09:40 +00:00
parent 732e2afb42
commit 83495f44af
15 changed files with 385 additions and 374 deletions

View File

@ -0,0 +1,3 @@
import os.path
package_root = os.path.dirname(os.path.abspath(__file__))

View File

@ -13,19 +13,19 @@ class AESCipher:
self.__padamt = 16
self.__key = hashlib.sha256(key.encode('utf-8')).digest()
def __pad(self, s: str) -> str:
def _pad(self, s: str) -> str:
intermediate = f"{len(s)}.{s}"
while len(intermediate) % self.__padamt != 0:
intermediate = intermediate + '-'
return intermediate
def __unpad(self, s: str) -> str:
def _unpad(self, s: str) -> str:
length, string = s.split('.', 1)
intlength = int(length)
return string[:intlength]
def encrypt(self, raw: str) -> str:
raw = self.__pad(raw)
raw = self._pad(raw)
random = Random.new()
iv = random.read(AES.block_size)
cipher = AES.new(self.__key, AES.MODE_CBC, iv)
@ -35,4 +35,4 @@ class AESCipher:
enc = base64.b64decode(encoded.encode('utf-8'), altchars=b"._")
iv = enc[:AES.block_size]
cipher = AES.new(self.__key, AES.MODE_CBC, iv)
return self.__unpad(cipher.decrypt(enc[AES.block_size:]).decode('utf-8'))
return self._unpad(cipher.decrypt(enc[AES.block_size:]).decode('utf-8'))

View File

@ -1,4 +1,4 @@
from typing import List
from typing import Dict, Final, List
class CardCipherException(Exception):
@ -13,7 +13,7 @@ class CardCipher:
Tau and converted ham-fistedly to Python.
"""
KEY = [
KEY: Final[List[int]] = [
0x20d0d03c, 0x868ecb41, 0xbcd89c84, 0x4c0e0d0d,
0x84fc30ac, 0x4cc1890e, 0xfc5418a4, 0x02c50f44,
0x68acb4e0, 0x06cd4a4e, 0xcc28906c, 0x4f0c8ac0,
@ -40,7 +40,7 @@ class CardCipher:
0x60443ce4, 0x4c0b8b8d, 0xe054e8bc, 0x02008e89,
]
LUT_A0 = [
LUT_A0: Final[List[int]] = [
0x02080008, 0x02082000, 0x00002008, 0x00000000,
0x02002000, 0x00080008, 0x02080000, 0x02082008,
0x00000008, 0x02000000, 0x00082000, 0x00002008,
@ -59,7 +59,7 @@ class CardCipher:
0x02000000, 0x02080008, 0x00002000, 0x00082008,
]
LUT_A1 = [
LUT_A1: Final[List[int]] = [
0x08000004, 0x00020004, 0x00000000, 0x08020200,
0x00020004, 0x00000200, 0x08000204, 0x00020000,
0x00000204, 0x08020204, 0x00020200, 0x08000000,
@ -78,7 +78,7 @@ class CardCipher:
0x08020000, 0x00000204, 0x00000004, 0x08020004,
]
LUT_A2 = [
LUT_A2: Final[List[int]] = [
0x80040100, 0x01000100, 0x80000000, 0x81040100,
0x00000000, 0x01040000, 0x81000100, 0x80040000,
0x01040100, 0x81000000, 0x01000000, 0x80000100,
@ -97,7 +97,7 @@ class CardCipher:
0x80000100, 0x01000000, 0x81000000, 0x01040100,
]
LUT_A3 = [
LUT_A3: Final[List[int]] = [
0x04010801, 0x00000000, 0x00010800, 0x04010000,
0x04000001, 0x00000801, 0x04000800, 0x00010800,
0x00000800, 0x04010001, 0x00000001, 0x04000800,
@ -116,7 +116,7 @@ class CardCipher:
0x04000801, 0x00000001, 0x04010000, 0x00010800,
]
LUT_B0 = [
LUT_B0: Final[List[int]] = [
0x00000400, 0x00000020, 0x00100020, 0x40100000,
0x40100420, 0x40000400, 0x00000420, 0x00000000,
0x00100000, 0x40100020, 0x40000020, 0x00100400,
@ -135,7 +135,7 @@ class CardCipher:
0x40100000, 0x00100420, 0x00100400, 0x40000400,
]
LUT_B1 = [
LUT_B1: Final[List[int]] = [
0x00800000, 0x00001000, 0x00000040, 0x00801042,
0x00801002, 0x00800040, 0x00001042, 0x00801000,
0x00001000, 0x00000002, 0x00800002, 0x00001040,
@ -154,7 +154,7 @@ class CardCipher:
0x00000002, 0x00001042, 0x00801000, 0x00800002,
]
LUT_B2 = [
LUT_B2: Final[List[int]] = [
0x10400000, 0x00404010, 0x00000010, 0x10400010,
0x10004000, 0x00400000, 0x10400010, 0x00004010,
0x00400010, 0x00004000, 0x00404000, 0x10000000,
@ -173,7 +173,7 @@ class CardCipher:
0x10400000, 0x10004010, 0x00000010, 0x00404000,
]
LUT_B3 = [
LUT_B3: Final[List[int]] = [
0x00208080, 0x00008000, 0x20200000, 0x20208080,
0x00200000, 0x20008080, 0x20008000, 0x20200000,
0x20008080, 0x00208080, 0x00208000, 0x20000080,
@ -192,8 +192,8 @@ class CardCipher:
0x20000000, 0x20208000, 0x00000080, 0x20008080,
]
VALID_CHARS = "0123456789ABCDEFGHJKLMNPRSTUWXYZ"
CONV_CHARS = {
VALID_CHARS: Final[str] = "0123456789ABCDEFGHJKLMNPRSTUWXYZ"
CONV_CHARS: Final[Dict[str, str]] = {
"I": "1",
"O": "0",
}
@ -231,7 +231,7 @@ class CardCipher:
reverse[7 - i] = cardint[i]
# Encipher
ciphered = CardCipher.__encode(bytes(reverse))
ciphered = CardCipher._encode(bytes(reverse))
# Convert 8 x 8 bit bytes into 13 x 5 bit groups (sort of)
bits = [0] * 65
@ -330,7 +330,7 @@ class CardCipher:
ciphered[int(i / 8)] |= bits[i] << (~i & 7)
# Decipher and reverse
deciphered = CardCipher.__decode(bytes(ciphered))
deciphered = CardCipher._decode(bytes(ciphered))
reverse = [0] * 8
for i in range(0, 8):
reverse[i] = deciphered[7 - i]
@ -362,7 +362,7 @@ class CardCipher:
return checksum
@staticmethod
def __encode(inbytes: bytes) -> bytes:
def _encode(inbytes: bytes) -> bytes:
if len(inbytes) != 8:
raise CardCipherException(
f'Expected 8-byte input, got {len(inbytes)}',
@ -378,7 +378,7 @@ class CardCipher:
return bytes(out)
@staticmethod
def __decode(inbytes: bytes) -> bytes:
def _decode(inbytes: bytes) -> bytes:
if len(inbytes) != 8:
raise CardCipherException(
f'Expected 8-byte input, got {len(inbytes)}',

View File

@ -1,124 +1,127 @@
from typing import Final
class GameConstants:
BISHI_BASHI = 'bishi'
DANCE_EVOLUTION = 'danevo'
DDR = 'ddr'
IIDX = 'iidx'
JUBEAT = 'jubeat'
MUSECA = 'museca'
POPN_MUSIC = 'pnm'
REFLEC_BEAT = 'reflec'
SDVX = 'sdvx'
BISHI_BASHI: Final[str] = 'bishi'
DANCE_EVOLUTION: Final[str] = 'danevo'
DDR: Final[str] = 'ddr'
IIDX: Final[str] = 'iidx'
JUBEAT: Final[str] = 'jubeat'
MUSECA: Final[str] = 'museca'
POPN_MUSIC: Final[str] = 'pnm'
REFLEC_BEAT: Final[str] = 'reflec'
SDVX: Final[str] = 'sdvx'
class VersionConstants:
BISHI_BASHI_TSBB = 1
BISHI_BASHI_TSBB: Final[int] = 1
DDR_1STMIX = 1
DDR_2NDMIX = 2
DDR_3RDMIX = 3
DDR_4THMIX = 4
DDR_5THMIX = 5
DDR_6THMIX = 6
DDR_7THMIX = 7
DDR_EXTREME = 8
DDR_SUPERNOVA = 9
DDR_SUPERNOVA_2 = 10
DDR_X = 11
DDR_X2 = 12
DDR_X3_VS_2NDMIX = 13
DDR_2013 = 14
DDR_2014 = 15
DDR_ACE = 16
DDR_A20 = 17
DDR_1STMIX: Final[int] = 1
DDR_2NDMIX: Final[int] = 2
DDR_3RDMIX: Final[int] = 3
DDR_4THMIX: Final[int] = 4
DDR_5THMIX: Final[int] = 5
DDR_6THMIX: Final[int] = 6
DDR_7THMIX: Final[int] = 7
DDR_EXTREME: Final[int] = 8
DDR_SUPERNOVA: Final[int] = 9
DDR_SUPERNOVA_2: Final[int] = 10
DDR_X: Final[int] = 11
DDR_X2: Final[int] = 12
DDR_X3_VS_2NDMIX: Final[int] = 13
DDR_2013: Final[int] = 14
DDR_2014: Final[int] = 15
DDR_ACE: Final[int] = 16
DDR_A20: Final[int] = 17
IIDX = 1
IIDX_2ND_STYLE = 2
IIDX_3RD_STYLE = 3
IIDX_4TH_STYLE = 4
IIDX_5TH_STYLE = 5
IIDX_6TH_STYLE = 6
IIDX_7TH_STYLE = 7
IIDX_8TH_STYLE = 8
IIDX_9TH_STYLE = 9
IIDX_10TH_STYLE = 10
IIDX_RED = 11
IIDX_HAPPY_SKY = 12
IIDX_DISTORTED = 13
IIDX_GOLD = 14
IIDX_DJ_TROOPERS = 15
IIDX_EMPRESS = 16
IIDX_SIRIUS = 17
IIDX_RESORT_ANTHEM = 18
IIDX_LINCLE = 19
IIDX_TRICORO = 20
IIDX_SPADA = 21
IIDX_PENDUAL = 22
IIDX_COPULA = 23
IIDX_SINOBUZ = 24
IIDX_CANNON_BALLERS = 25
IIDX: Final[int] = 1
IIDX_2ND_STYLE: Final[int] = 2
IIDX_3RD_STYLE: Final[int] = 3
IIDX_4TH_STYLE: Final[int] = 4
IIDX_5TH_STYLE: Final[int] = 5
IIDX_6TH_STYLE: Final[int] = 6
IIDX_7TH_STYLE: Final[int] = 7
IIDX_8TH_STYLE: Final[int] = 8
IIDX_9TH_STYLE: Final[int] = 9
IIDX_10TH_STYLE: Final[int] = 10
IIDX_RED: Final[int] = 11
IIDX_HAPPY_SKY: Final[int] = 12
IIDX_DISTORTED: Final[int] = 13
IIDX_GOLD: Final[int] = 14
IIDX_DJ_TROOPERS: Final[int] = 15
IIDX_EMPRESS: Final[int] = 16
IIDX_SIRIUS: Final[int] = 17
IIDX_RESORT_ANTHEM: Final[int] = 18
IIDX_LINCLE: Final[int] = 19
IIDX_TRICORO: Final[int] = 20
IIDX_SPADA: Final[int] = 21
IIDX_PENDUAL: Final[int] = 22
IIDX_COPULA: Final[int] = 23
IIDX_SINOBUZ: Final[int] = 24
IIDX_CANNON_BALLERS: Final[int] = 25
JUBEAT = 1
JUBEAT_RIPPLES = 2
JUBEAT_RIPPLES_APPEND = 3
JUBEAT_KNIT = 4
JUBEAT_KNIT_APPEND = 5
JUBEAT_COPIOUS = 6
JUBEAT_COPIOUS_APPEND = 7
JUBEAT_SAUCER = 8
JUBEAT_SAUCER_FULFILL = 9
JUBEAT_PROP = 10
JUBEAT_QUBELL = 11
JUBEAT_CLAN = 12
JUBEAT_FESTO = 13
JUBEAT: Final[int] = 1
JUBEAT_RIPPLES: Final[int] = 2
JUBEAT_RIPPLES_APPEND: Final[int] = 3
JUBEAT_KNIT: Final[int] = 4
JUBEAT_KNIT_APPEND: Final[int] = 5
JUBEAT_COPIOUS: Final[int] = 6
JUBEAT_COPIOUS_APPEND: Final[int] = 7
JUBEAT_SAUCER: Final[int] = 8
JUBEAT_SAUCER_FULFILL: Final[int] = 9
JUBEAT_PROP: Final[int] = 10
JUBEAT_QUBELL: Final[int] = 11
JUBEAT_CLAN: Final[int] = 12
JUBEAT_FESTO: Final[int] = 13
MUSECA = 1
MUSECA_1_PLUS = 2
MUSECA: Final[int] = 1
MUSECA_1_PLUS: Final[int] = 2
POPN_MUSIC = 1
POPN_MUSIC_2 = 2
POPN_MUSIC_3 = 3
POPN_MUSIC_4 = 4
POPN_MUSIC_5 = 5
POPN_MUSIC_6 = 6
POPN_MUSIC_7 = 7
POPN_MUSIC_8 = 8
POPN_MUSIC_9 = 9
POPN_MUSIC_10 = 10
POPN_MUSIC_11 = 11
POPN_MUSIC_IROHA = 12
POPN_MUSIC_CARNIVAL = 13
POPN_MUSIC_FEVER = 14
POPN_MUSIC_ADVENTURE = 15
POPN_MUSIC_PARTY = 16
POPN_MUSIC_THE_MOVIE = 17
POPN_MUSIC_SENGOKU_RETSUDEN = 18
POPN_MUSIC_TUNE_STREET = 19
POPN_MUSIC_FANTASIA = 20
POPN_MUSIC_SUNNY_PARK = 21
POPN_MUSIC_LAPISTORIA = 22
POPN_MUSIC_ECLALE = 23
POPN_MUSIC_USANEKO = 24
POPN_MUSIC_PEACE = 25
POPN_MUSIC: Final[int] = 1
POPN_MUSIC_2: Final[int] = 2
POPN_MUSIC_3: Final[int] = 3
POPN_MUSIC_4: Final[int] = 4
POPN_MUSIC_5: Final[int] = 5
POPN_MUSIC_6: Final[int] = 6
POPN_MUSIC_7: Final[int] = 7
POPN_MUSIC_8: Final[int] = 8
POPN_MUSIC_9: Final[int] = 9
POPN_MUSIC_10: Final[int] = 10
POPN_MUSIC_11: Final[int] = 11
POPN_MUSIC_IROHA: Final[int] = 12
POPN_MUSIC_CARNIVAL: Final[int] = 13
POPN_MUSIC_FEVER: Final[int] = 14
POPN_MUSIC_ADVENTURE: Final[int] = 15
POPN_MUSIC_PARTY: Final[int] = 16
POPN_MUSIC_THE_MOVIE: Final[int] = 17
POPN_MUSIC_SENGOKU_RETSUDEN: Final[int] = 18
POPN_MUSIC_TUNE_STREET: Final[int] = 19
POPN_MUSIC_FANTASIA: Final[int] = 20
POPN_MUSIC_SUNNY_PARK: Final[int] = 21
POPN_MUSIC_LAPISTORIA: Final[int] = 22
POPN_MUSIC_ECLALE: Final[int] = 23
POPN_MUSIC_USANEKO: Final[int] = 24
POPN_MUSIC_PEACE: Final[int] = 25
REFLEC_BEAT = 1
REFLEC_BEAT_LIMELIGHT = 2
REFLEC_BEAT_COLETTE = 3
REFLEC_BEAT_GROOVIN = 4
REFLEC_BEAT_VOLZZA = 5
REFLEC_BEAT_VOLZZA_2 = 6
REFLEC_BEAT_REFLESIA = 7
REFLEC_BEAT: Final[int] = 1
REFLEC_BEAT_LIMELIGHT: Final[int] = 2
REFLEC_BEAT_COLETTE: Final[int] = 3
REFLEC_BEAT_GROOVIN: Final[int] = 4
REFLEC_BEAT_VOLZZA: Final[int] = 5
REFLEC_BEAT_VOLZZA_2: Final[int] = 6
REFLEC_BEAT_REFLESIA: Final[int] = 7
SDVX_BOOTH = 1
SDVX_INFINITE_INFECTION = 2
SDVX_GRAVITY_WARS = 3
SDVX_HEAVENLY_HAVEN = 4
SDVX_BOOTH: Final[int] = 1
SDVX_INFINITE_INFECTION: Final[int] = 2
SDVX_GRAVITY_WARS: Final[int] = 3
SDVX_HEAVENLY_HAVEN: Final[int] = 4
class APIConstants:
ID_TYPE_SERVER = 'server'
ID_TYPE_CARD = 'card'
ID_TYPE_SONG = 'song'
ID_TYPE_INSTANCE = 'instance'
ID_TYPE_SERVER: Final[str] = 'server'
ID_TYPE_CARD: Final[str] = 'card'
ID_TYPE_SONG: Final[str] = 'song'
ID_TYPE_INSTANCE: Final[str] = 'instance'
class DBConstants:
@ -140,114 +143,114 @@ class DBConstants:
# will only be found in the DB itself, as well as used on the frontend
# to display various general information about scores.
OMNIMIX_VERSION_BUMP = 10000
OMNIMIX_VERSION_BUMP: Final[int] = 10000
DDR_HALO_NONE = 100
DDR_HALO_GOOD_FULL_COMBO = 200
DDR_HALO_GREAT_FULL_COMBO = 300
DDR_HALO_PERFECT_FULL_COMBO = 400
DDR_HALO_MARVELOUS_FULL_COMBO = 500
DDR_RANK_E = 100
DDR_RANK_D = 200
DDR_RANK_D_PLUS = 233
DDR_RANK_C_MINUS = 266
DDR_RANK_C = 300
DDR_RANK_C_PLUS = 333
DDR_RANK_B_MINUS = 366
DDR_RANK_B = 400
DDR_RANK_B_PLUS = 433
DDR_RANK_A_MINUS = 466
DDR_RANK_A = 500
DDR_RANK_A_PLUS = 533
DDR_RANK_AA_MINUS = 566
DDR_RANK_AA = 600
DDR_RANK_AA_PLUS = 650
DDR_RANK_AAA = 700
DDR_HALO_NONE: Final[int] = 100
DDR_HALO_GOOD_FULL_COMBO: Final[int] = 200
DDR_HALO_GREAT_FULL_COMBO: Final[int] = 300
DDR_HALO_PERFECT_FULL_COMBO: Final[int] = 400
DDR_HALO_MARVELOUS_FULL_COMBO: Final[int] = 500
DDR_RANK_E: Final[int] = 100
DDR_RANK_D: Final[int] = 200
DDR_RANK_D_PLUS: Final[int] = 233
DDR_RANK_C_MINUS: Final[int] = 266
DDR_RANK_C: Final[int] = 300
DDR_RANK_C_PLUS: Final[int] = 333
DDR_RANK_B_MINUS: Final[int] = 366
DDR_RANK_B: Final[int] = 400
DDR_RANK_B_PLUS: Final[int] = 433
DDR_RANK_A_MINUS: Final[int] = 466
DDR_RANK_A: Final[int] = 500
DDR_RANK_A_PLUS: Final[int] = 533
DDR_RANK_AA_MINUS: Final[int] = 566
DDR_RANK_AA: Final[int] = 600
DDR_RANK_AA_PLUS: Final[int] = 650
DDR_RANK_AAA: Final[int] = 700
IIDX_CLEAR_STATUS_NO_PLAY = 50
IIDX_CLEAR_STATUS_FAILED = 100
IIDX_CLEAR_STATUS_ASSIST_CLEAR = 200
IIDX_CLEAR_STATUS_EASY_CLEAR = 300
IIDX_CLEAR_STATUS_CLEAR = 400
IIDX_CLEAR_STATUS_HARD_CLEAR = 500
IIDX_CLEAR_STATUS_EX_HARD_CLEAR = 600
IIDX_CLEAR_STATUS_FULL_COMBO = 700
IIDX_DAN_RANK_7_KYU = 100
IIDX_DAN_RANK_6_KYU = 200
IIDX_DAN_RANK_5_KYU = 300
IIDX_DAN_RANK_4_KYU = 400
IIDX_DAN_RANK_3_KYU = 500
IIDX_DAN_RANK_2_KYU = 600
IIDX_DAN_RANK_1_KYU = 700
IIDX_DAN_RANK_1_DAN = 800
IIDX_DAN_RANK_2_DAN = 900
IIDX_DAN_RANK_3_DAN = 1000
IIDX_DAN_RANK_4_DAN = 1100
IIDX_DAN_RANK_5_DAN = 1200
IIDX_DAN_RANK_6_DAN = 1300
IIDX_DAN_RANK_7_DAN = 1400
IIDX_DAN_RANK_8_DAN = 1500
IIDX_DAN_RANK_9_DAN = 1600
IIDX_DAN_RANK_10_DAN = 1700
IIDX_DAN_RANK_CHUDEN = 1800
IIDX_DAN_RANK_KAIDEN = 1900
IIDX_CLEAR_STATUS_NO_PLAY: Final[int] = 50
IIDX_CLEAR_STATUS_FAILED: Final[int] = 100
IIDX_CLEAR_STATUS_ASSIST_CLEAR: Final[int] = 200
IIDX_CLEAR_STATUS_EASY_CLEAR: Final[int] = 300
IIDX_CLEAR_STATUS_CLEAR: Final[int] = 400
IIDX_CLEAR_STATUS_HARD_CLEAR: Final[int] = 500
IIDX_CLEAR_STATUS_EX_HARD_CLEAR: Final[int] = 600
IIDX_CLEAR_STATUS_FULL_COMBO: Final[int] = 700
IIDX_DAN_RANK_7_KYU: Final[int] = 100
IIDX_DAN_RANK_6_KYU: Final[int] = 200
IIDX_DAN_RANK_5_KYU: Final[int] = 300
IIDX_DAN_RANK_4_KYU: Final[int] = 400
IIDX_DAN_RANK_3_KYU: Final[int] = 500
IIDX_DAN_RANK_2_KYU: Final[int] = 600
IIDX_DAN_RANK_1_KYU: Final[int] = 700
IIDX_DAN_RANK_1_DAN: Final[int] = 800
IIDX_DAN_RANK_2_DAN: Final[int] = 900
IIDX_DAN_RANK_3_DAN: Final[int] = 1000
IIDX_DAN_RANK_4_DAN: Final[int] = 1100
IIDX_DAN_RANK_5_DAN: Final[int] = 1200
IIDX_DAN_RANK_6_DAN: Final[int] = 1300
IIDX_DAN_RANK_7_DAN: Final[int] = 1400
IIDX_DAN_RANK_8_DAN: Final[int] = 1500
IIDX_DAN_RANK_9_DAN: Final[int] = 1600
IIDX_DAN_RANK_10_DAN: Final[int] = 1700
IIDX_DAN_RANK_CHUDEN: Final[int] = 1800
IIDX_DAN_RANK_KAIDEN: Final[int] = 1900
JUBEAT_PLAY_MEDAL_FAILED = 100
JUBEAT_PLAY_MEDAL_CLEARED = 200
JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO = 300
JUBEAT_PLAY_MEDAL_FULL_COMBO = 400
JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT = 500
JUBEAT_PLAY_MEDAL_EXCELLENT = 600
JUBEAT_PLAY_MEDAL_FAILED: Final[int] = 100
JUBEAT_PLAY_MEDAL_CLEARED: Final[int] = 200
JUBEAT_PLAY_MEDAL_NEARLY_FULL_COMBO: Final[int] = 300
JUBEAT_PLAY_MEDAL_FULL_COMBO: Final[int] = 400
JUBEAT_PLAY_MEDAL_NEARLY_EXCELLENT: Final[int] = 500
JUBEAT_PLAY_MEDAL_EXCELLENT: Final[int] = 600
MUSECA_GRADE_DEATH = 100 # 没
MUSECA_GRADE_POOR = 200 # 拙
MUSECA_GRADE_MEDIOCRE = 300 # 凡
MUSECA_GRADE_GOOD = 400 # 佳
MUSECA_GRADE_GREAT = 500 # 良
MUSECA_GRADE_EXCELLENT = 600 # 優
MUSECA_GRADE_SUPERB = 700 # 秀
MUSECA_GRADE_MASTERPIECE = 800 # 傑
MUSECA_GRADE_PERFECT = 900 # 傑
MUSECA_CLEAR_TYPE_FAILED = 100
MUSECA_CLEAR_TYPE_CLEARED = 200
MUSECA_CLEAR_TYPE_FULL_COMBO = 300
MUSECA_GRADE_DEATH: Final[int] = 100 # 没
MUSECA_GRADE_POOR: Final[int] = 200 # 拙
MUSECA_GRADE_MEDIOCRE: Final[int] = 300 # 凡
MUSECA_GRADE_GOOD: Final[int] = 400 # 佳
MUSECA_GRADE_GREAT: Final[int] = 500 # 良
MUSECA_GRADE_EXCELLENT: Final[int] = 600 # 優
MUSECA_GRADE_SUPERB: Final[int] = 700 # 秀
MUSECA_GRADE_MASTERPIECE: Final[int] = 800 # 傑
MUSECA_GRADE_PERFECT: Final[int] = 900 # 傑
MUSECA_CLEAR_TYPE_FAILED: Final[int] = 100
MUSECA_CLEAR_TYPE_CLEARED: Final[int] = 200
MUSECA_CLEAR_TYPE_FULL_COMBO: Final[int] = 300
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED = 100
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED = 200
POPN_MUSIC_PLAY_MEDAL_STAR_FAILED = 300
POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR = 400
POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED = 500
POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED = 600
POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED = 700
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO = 800
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO = 900
POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO = 1000
POPN_MUSIC_PLAY_MEDAL_PERFECT = 1100
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FAILED: Final[int] = 100
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FAILED: Final[int] = 200
POPN_MUSIC_PLAY_MEDAL_STAR_FAILED: Final[int] = 300
POPN_MUSIC_PLAY_MEDAL_EASY_CLEAR: Final[int] = 400
POPN_MUSIC_PLAY_MEDAL_CIRCLE_CLEARED: Final[int] = 500
POPN_MUSIC_PLAY_MEDAL_DIAMOND_CLEARED: Final[int] = 600
POPN_MUSIC_PLAY_MEDAL_STAR_CLEARED: Final[int] = 700
POPN_MUSIC_PLAY_MEDAL_CIRCLE_FULL_COMBO: Final[int] = 800
POPN_MUSIC_PLAY_MEDAL_DIAMOND_FULL_COMBO: Final[int] = 900
POPN_MUSIC_PLAY_MEDAL_STAR_FULL_COMBO: Final[int] = 1000
POPN_MUSIC_PLAY_MEDAL_PERFECT: Final[int] = 1100
REFLEC_BEAT_CLEAR_TYPE_NO_PLAY = 100
REFLEC_BEAT_CLEAR_TYPE_FAILED = 200
REFLEC_BEAT_CLEAR_TYPE_CLEARED = 300
REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED = 400
REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED = 500
REFLEC_BEAT_COMBO_TYPE_NONE = 100
REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO = 200
REFLEC_BEAT_COMBO_TYPE_FULL_COMBO = 300
REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST = 400
REFLEC_BEAT_CLEAR_TYPE_NO_PLAY: Final[int] = 100
REFLEC_BEAT_CLEAR_TYPE_FAILED: Final[int] = 200
REFLEC_BEAT_CLEAR_TYPE_CLEARED: Final[int] = 300
REFLEC_BEAT_CLEAR_TYPE_HARD_CLEARED: Final[int] = 400
REFLEC_BEAT_CLEAR_TYPE_S_HARD_CLEARED: Final[int] = 500
REFLEC_BEAT_COMBO_TYPE_NONE: Final[int] = 100
REFLEC_BEAT_COMBO_TYPE_ALMOST_COMBO: Final[int] = 200
REFLEC_BEAT_COMBO_TYPE_FULL_COMBO: Final[int] = 300
REFLEC_BEAT_COMBO_TYPE_FULL_COMBO_ALL_JUST: Final[int] = 400
SDVX_CLEAR_TYPE_NO_PLAY = 50
SDVX_CLEAR_TYPE_FAILED = 100
SDVX_CLEAR_TYPE_CLEAR = 200
SDVX_CLEAR_TYPE_HARD_CLEAR = 300
SDVX_CLEAR_TYPE_ULTIMATE_CHAIN = 400
SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN = 500
SDVX_GRADE_NO_PLAY = 100
SDVX_GRADE_D = 200
SDVX_GRADE_C = 300
SDVX_GRADE_B = 400
SDVX_GRADE_A = 500
SDVX_GRADE_A_PLUS = 550
SDVX_GRADE_AA = 600
SDVX_GRADE_AA_PLUS = 650
SDVX_GRADE_AAA = 700
SDVX_GRADE_AAA_PLUS = 800
SDVX_GRADE_S = 900
SDVX_CLEAR_TYPE_NO_PLAY: Final[int] = 50
SDVX_CLEAR_TYPE_FAILED: Final[int] = 100
SDVX_CLEAR_TYPE_CLEAR: Final[int] = 200
SDVX_CLEAR_TYPE_HARD_CLEAR: Final[int] = 300
SDVX_CLEAR_TYPE_ULTIMATE_CHAIN: Final[int] = 400
SDVX_CLEAR_TYPE_PERFECT_ULTIMATE_CHAIN: Final[int] = 500
SDVX_GRADE_NO_PLAY: Final[int] = 100
SDVX_GRADE_D: Final[int] = 200
SDVX_GRADE_C: Final[int] = 300
SDVX_GRADE_B: Final[int] = 400
SDVX_GRADE_A: Final[int] = 500
SDVX_GRADE_A_PLUS: Final[int] = 550
SDVX_GRADE_AA: Final[int] = 600
SDVX_GRADE_AA_PLUS: Final[int] = 650
SDVX_GRADE_AAA: Final[int] = 700
SDVX_GRADE_AAA_PLUS: Final[int] = 800
SDVX_GRADE_S: Final[int] = 900

View File

@ -2,7 +2,7 @@ import calendar
import datetime
from dateutil import tz
from typing import List, Optional
from typing import List, Final, Optional
class Time:
@ -11,10 +11,10 @@ class Time:
standard unix timestamps at UTC timezone given various parameters.
"""
SECONDS_IN_MINUTE = 60
SECONDS_IN_HOUR = 3600
SECONDS_IN_DAY = 86400
SECONDS_IN_WEEK = 604800
SECONDS_IN_MINUTE: Final[int] = 60
SECONDS_IN_HOUR: Final[int] = 3600
SECONDS_IN_DAY: Final[int] = 86400
SECONDS_IN_WEEK: Final[int] = 604800
@staticmethod
def now() -> int:

View File

@ -1,5 +1,5 @@
import struct
from typing import Optional, List, Dict, Any
from typing import Optional, Final, List, Dict, Any
from bemani.protocol.stream import InputStream, OutputStream
from bemani.protocol.node import Node
@ -708,17 +708,17 @@ class BinaryEncoding:
"""
Wrapper class representing a Binary Encoding.
"""
MAGIC = 0xA0
MAGIC: Final[int] = 0xA0
COMPRESSED_WITH_DATA = 0x42
COMPRESSED_WITHOUT_DATA = 0x43
DECOMPRESSED_WITH_DATA = 0x45
DECOMPRESSED_WITHOUT_DATA = 0x46
COMPRESSED_WITH_DATA: Final[int] = 0x42
COMPRESSED_WITHOUT_DATA: Final[int] = 0x43
DECOMPRESSED_WITH_DATA: Final[int] = 0x45
DECOMPRESSED_WITHOUT_DATA: Final[int] = 0x46
# The string values should match the constants in EAmuseProtocol.
# I have no better way to link these than to write this comment,
# as otherwise we would have a circular dependency.
ENCODINGS = {
ENCODINGS: Final[Dict[int, str]] = {
0x00: "ascii",
0x20: "shift-jis-legacy",
0x60: "euc-jp",

View File

@ -1,13 +1,15 @@
import ctypes
import os
from collections import defaultdict
from typing import Generator, List, MutableMapping, Optional, Set, Tuple
from typing import Generator, Final, List, MutableMapping, Optional, Set, Tuple
from .. import package_root
# Attempt to use the faster C++ libraries if they're available
try:
clib = None
clib_path = os.path.dirname(os.path.abspath(__file__))
clib_path = os.path.join(package_root, "protocol")
files = [f for f in os.listdir(clib_path) if f.startswith("lz77cpp") and f.endswith(".so")]
if len(files) > 0:
clib = ctypes.cdll.LoadLibrary(os.path.join(clib_path, files[0]))
@ -32,10 +34,10 @@ class Lz77Decompress:
over-the-wire compression of XML data, as well as compression inside a decent
amount of file formats found in various Konami games.
"""
RING_LENGTH = 0x1000
RING_LENGTH: Final[int] = 0x1000
FLAG_COPY = 1
FLAG_BACKREF = 0
FLAG_COPY: Final[int] = 1
FLAG_BACKREF: Final[int] = 0
def __init__(self, data: bytes, backref: Optional[int] = None) -> None:
"""
@ -56,7 +58,7 @@ class Lz77Decompress:
self.ringlength: int = backref or self.RING_LENGTH
self.ring: bytes = b'\x00' * self.ringlength
def __ring_read(self, copy_pos: int, copy_len: int) -> Generator[bytes, None, None]:
def _ring_read(self, copy_pos: int, copy_len: int) -> Generator[bytes, None, None]:
"""
Read the next bytes from the backref ring at the current copy position.
@ -72,13 +74,13 @@ class Lz77Decompress:
amount = copy_len
ret = self.ring[copy_pos:(copy_pos + amount)]
self.__ring_write(ret)
self._ring_write(ret)
yield ret
copy_pos = (copy_pos + amount) % self.ringlength
copy_len -= amount
def __ring_write(self, bytedata: bytes) -> None:
def _ring_write(self, bytedata: bytes) -> None:
"""
Write bytes into the backref ring.
@ -117,7 +119,7 @@ class Lz77Decompress:
# ringbuffer). So, since we read that last time and wrote it to the backbuffer
# we are safe to read again.
amount = min(self.pending_copy_amount, self.pending_copy_max)
yield from self.__ring_read(self.pending_copy_pos, amount)
yield from self._ring_read(self.pending_copy_pos, amount)
# We read this many bytes and are about to write them to the ringbuffer,
# so bookkeep that.
@ -149,17 +151,17 @@ class Lz77Decompress:
# Grab chunk right out of the data source
b = self.data[self.read_pos:(self.read_pos + amount)]
self.__ring_write(b)
self._ring_write(b)
yield b
self.read_pos += amount
self.left -= amount
elif flag == self.FLAG_BACKREF:
yield from self.__read_backref()
yield from self._read_backref()
else:
raise Exception("Logic error!")
def __read_backref(self) -> Generator[bytes, None, None]:
def _read_backref(self) -> Generator[bytes, None, None]:
"""
Read a backref chunk. Grab the copy length and copy position
from the first two bytes and then read the first byte from
@ -204,7 +206,7 @@ class Lz77Decompress:
while copy_pos < 0:
copy_pos += self.ringlength
copy_pos = copy_pos % self.ringlength
yield from self.__ring_read(copy_pos, copy_len)
yield from self._ring_read(copy_pos, copy_len)
else:
self.eof = True
return
@ -221,12 +223,12 @@ class Lz77Compress:
once, and if we use a proxy to direct traffic, possibly a second time.
"""
RING_LENGTH = 0x1000
RING_LENGTH: Final[int] = 0x1000
LOOSE_COMPRESS_THRESHOLD = 1024 * 512
LOOSE_COMPRESS_THRESHOLD: Final[int] = 1024 * 512
FLAG_COPY = 1
FLAG_BACKREF = 0
FLAG_COPY: Final[int] = 1
FLAG_BACKREF: Final[int] = 0
def __init__(self, data: bytes, backref: Optional[int] = None) -> None:
"""
@ -246,11 +248,11 @@ class Lz77Compress:
self.last_start: Tuple[int, int, int] = (0, 0, 0)
if len(data) > self.LOOSE_COMPRESS_THRESHOLD:
self.__ring_write = self.__ring_write_starts_only
self._ring_write = self._ring_write_starts_only
else:
self.__ring_write = self.__ring_write_both
self._ring_write = self._ring_write_both
def __ring_write_starts_only(self, bytedata: bytes) -> None:
def _ring_write_starts_only(self, bytedata: bytes) -> None:
"""
Write bytes into the backref ring.
@ -266,7 +268,7 @@ class Lz77Compress:
# Keep track of the fact that we wrote this byte.
self.bytes_written += 1
def __ring_write_both(self, bytedata: bytes) -> None:
def _ring_write_both(self, bytedata: bytes) -> None:
"""
Write bytes into the backref ring.
@ -321,7 +323,7 @@ class Lz77Compress:
chunk = self.data[self.read_pos:(self.read_pos + 1)]
data[flagpos] = chunk
self.__ring_write(chunk)
self._ring_write(chunk)
self.read_pos += 1
self.left -= 1
@ -346,7 +348,7 @@ class Lz77Compress:
chunk = self.data[self.read_pos:(self.read_pos + 1)]
data[flagpos] = chunk
self.__ring_write(chunk)
self._ring_write(chunk)
self.read_pos += 1
self.left -= 1
@ -356,7 +358,7 @@ class Lz77Compress:
# we're going to write at least these three bytes, so append it to the
# output buffer.
start_write_size = self.bytes_written
self.__ring_write(index)
self._ring_write(index)
copy_amount = 3
while copy_amount < backref_amount:
# First, let's see if we have any 3-wide chunks to consume.
@ -369,7 +371,7 @@ class Lz77Compress:
if new_backref_locations:
# Mark that we're copying an extra byte from the backref.
self.__ring_write(index)
self._ring_write(index)
copy_amount += 3
possible_backref_locations = new_backref_locations
else:
@ -388,7 +390,7 @@ class Lz77Compress:
break
# Mark that we're copying an extra byte from the backref.
self.__ring_write(self.data[(self.read_pos + copy_amount):(self.read_pos + copy_amount + 1)])
self._ring_write(self.data[(self.read_pos + copy_amount):(self.read_pos + copy_amount + 1)])
copy_amount += 1
possible_backref_locations = new_backref_locations

View File

@ -1,6 +1,11 @@
import copy
import struct
from typing import Any, Dict, List, Optional, Union
from typing import Any, Dict, Final, List, Optional, Union
# Hack to get around mypy's lack of scoping on types.
_renamed_float = float
_renamed_bool = bool
class NodeException(Exception):
@ -16,60 +21,60 @@ class Node:
supported for a node to not have a value or children. This also includes a decent amount of
constructor helper classmethods to make constructing a tree from source code easier.
"""
NODE_NAME_CHARS = "0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
NODE_NAME_CHARS: Final[str] = "0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz"
NODE_TYPE_VOID = 1
NODE_TYPE_S8 = 2
NODE_TYPE_U8 = 3
NODE_TYPE_S16 = 4
NODE_TYPE_U16 = 5
NODE_TYPE_S32 = 6
NODE_TYPE_U32 = 7
NODE_TYPE_S64 = 8
NODE_TYPE_U64 = 9
NODE_TYPE_BIN = 10
NODE_TYPE_STR = 11
NODE_TYPE_IP4 = 12
NODE_TYPE_TIME = 13
NODE_TYPE_FLOAT = 14
NODE_TYPE_DOUBLE = 15
NODE_TYPE_VOID: Final[int] = 1
NODE_TYPE_S8: Final[int] = 2
NODE_TYPE_U8: Final[int] = 3
NODE_TYPE_S16: Final[int] = 4
NODE_TYPE_U16: Final[int] = 5
NODE_TYPE_S32: Final[int] = 6
NODE_TYPE_U32: Final[int] = 7
NODE_TYPE_S64: Final[int] = 8
NODE_TYPE_U64: Final[int] = 9
NODE_TYPE_BIN: Final[int] = 10
NODE_TYPE_STR: Final[int] = 11
NODE_TYPE_IP4: Final[int] = 12
NODE_TYPE_TIME: Final[int] = 13
NODE_TYPE_FLOAT: Final[int] = 14
NODE_TYPE_DOUBLE: Final[int] = 15
NODE_TYPE_2S8 = 16
NODE_TYPE_2U8 = 17
NODE_TYPE_2S16 = 18
NODE_TYPE_2U16 = 19
NODE_TYPE_2S32 = 20
NODE_TYPE_2U32 = 21
NODE_TYPE_2S64 = 22
NODE_TYPE_2U64 = 23
NODE_TYPE_2FLOAT = 24
NODE_TYPE_2DOUBLE = 25
NODE_TYPE_2S8: Final[int] = 16
NODE_TYPE_2U8: Final[int] = 17
NODE_TYPE_2S16: Final[int] = 18
NODE_TYPE_2U16: Final[int] = 19
NODE_TYPE_2S32: Final[int] = 20
NODE_TYPE_2U32: Final[int] = 21
NODE_TYPE_2S64: Final[int] = 22
NODE_TYPE_2U64: Final[int] = 23
NODE_TYPE_2FLOAT: Final[int] = 24
NODE_TYPE_2DOUBLE: Final[int] = 25
NODE_TYPE_3S8 = 26
NODE_TYPE_3U8 = 27
NODE_TYPE_3S16 = 28
NODE_TYPE_3U16 = 29
NODE_TYPE_3S32 = 30
NODE_TYPE_3U32 = 31
NODE_TYPE_3S64 = 32
NODE_TYPE_3U64 = 33
NODE_TYPE_3FLOAT = 34
NODE_TYPE_3DOUBLE = 35
NODE_TYPE_3S8: Final[int] = 26
NODE_TYPE_3U8: Final[int] = 27
NODE_TYPE_3S16: Final[int] = 28
NODE_TYPE_3U16: Final[int] = 29
NODE_TYPE_3S32: Final[int] = 30
NODE_TYPE_3U32: Final[int] = 31
NODE_TYPE_3S64: Final[int] = 32
NODE_TYPE_3U64: Final[int] = 33
NODE_TYPE_3FLOAT: Final[int] = 34
NODE_TYPE_3DOUBLE: Final[int] = 35
NODE_TYPE_4S8 = 36
NODE_TYPE_4U8 = 37
NODE_TYPE_4S16 = 38
NODE_TYPE_4U16 = 39
NODE_TYPE_4S32 = 40
NODE_TYPE_4U32 = 41
NODE_TYPE_4S64 = 42
NODE_TYPE_4U64 = 43
NODE_TYPE_4FLOAT = 44
NODE_TYPE_4DOUBLE = 45
NODE_TYPE_4S8: Final[int] = 36
NODE_TYPE_4U8: Final[int] = 37
NODE_TYPE_4S16: Final[int] = 38
NODE_TYPE_4U16: Final[int] = 39
NODE_TYPE_4S32: Final[int] = 40
NODE_TYPE_4U32: Final[int] = 41
NODE_TYPE_4S64: Final[int] = 42
NODE_TYPE_4U64: Final[int] = 43
NODE_TYPE_4FLOAT: Final[int] = 44
NODE_TYPE_4DOUBLE: Final[int] = 45
NODE_TYPE_BOOL = 52
NODE_TYPE_BOOL: Final[int] = 52
NODE_TYPES = {
NODE_TYPES: Final[Dict[int, Dict[str, Any]]] = {
NODE_TYPE_VOID: {
'name': 'void',
'enc': '',
@ -347,10 +352,10 @@ class Node:
'composite': False,
},
}
ARRAY_BIT = 0x40
ATTR_TYPE = 0x2E
END_OF_NODE = 0xFE
END_OF_DOCUMENT = 0xFF
ARRAY_BIT: Final[int] = 0x40
ATTR_TYPE: Final[int] = 0x2E
END_OF_NODE: Final[int] = 0xFE
END_OF_DOCUMENT: Final[int] = 0xFF
@staticmethod
def void(name: str) -> 'Node':
@ -365,11 +370,11 @@ class Node:
return Node(name=name, type=Node.NODE_TYPE_BIN, value=value)
@staticmethod
def __float(name: str, value: float) -> 'Node':
def float(name: str, value: _renamed_float) -> 'Node':
return Node(name=name, type=Node.NODE_TYPE_FLOAT, value=value)
@staticmethod
def __bool(name: str, value: bool) -> 'Node':
def bool(name: str, value: _renamed_bool) -> 'Node':
return Node(name=name, type=Node.NODE_TYPE_BOOL, value=value)
@staticmethod
@ -425,11 +430,11 @@ class Node:
return Node(name=name, type=Node.NODE_TYPE_TIME, array=True, value=values)
@staticmethod
def float_array(name: str, values: List[float]) -> 'Node':
def float_array(name: str, values: List[_renamed_float]) -> 'Node':
return Node(name=name, type=Node.NODE_TYPE_FLOAT, array=True, value=values)
@staticmethod
def bool_array(name: str, values: List[bool]) -> 'Node':
def bool_array(name: str, values: List[_renamed_bool]) -> 'Node':
return Node(name=name, type=Node.NODE_TYPE_BOOL, array=True, value=values)
@staticmethod
@ -532,7 +537,7 @@ class Node:
if value < -9223372036854775808 or value > 9223372036854775807:
raise NodeException(f'Invalid value {value} for s32 {name}')
def __init__(self, name: Optional[str]=None, type: Optional[int]=None, array: Optional[bool]=None, value: Optional[Any]=None) -> None:
def __init__(self, name: Optional[str]=None, type: Optional[int]=None, array: Optional[_renamed_bool]=None, value: Optional[Any]=None) -> None:
"""
Initialize a node, with an optional name and type.
@ -589,7 +594,7 @@ class Node:
raise Exception('Logic error, tried to fetch name before setting!')
return self.__name
def set_type(self, type: int, array: Optional[bool]=None) -> None:
def set_type(self, type: int, array: Optional[_renamed_bool]=None) -> None:
"""
Set the type of the node to a new integer type, as specified in Node.NODE_TYPES.
@ -763,7 +768,7 @@ class Node:
return self.__attrs
@property
def is_array(self) -> bool:
def is_array(self) -> _renamed_bool:
"""
Wrapper for accessing array type.
@ -773,7 +778,7 @@ class Node:
return self.__array
@property
def is_composite(self) -> bool:
def is_composite(self) -> _renamed_bool:
"""
Returns whether or not this element is a composite type (basically
an array, but packed differently).
@ -905,7 +910,7 @@ class Node:
attrs_dict['__type'] = translated_type['name']
order.insert(0, '__type')
def escape(val: Any, attr: bool=False) -> str:
def escape(val: Any, attr: _renamed_bool=False) -> str:
if isinstance(val, str):
val = val.replace('&', '&amp;')
val = val.replace('<', '&lt;')
@ -977,7 +982,7 @@ class Node:
"""
return self.__to_xml(0)
def __eq__(self, other: object) -> bool:
def __eq__(self, other: object) -> _renamed_bool:
"""
Convenience function for comparing two nodes.
@ -1027,7 +1032,7 @@ class Node:
except Exception:
return False
def __ne__(self, other: object) -> bool:
def __ne__(self, other: object) -> _renamed_bool:
"""
Convenience function for comparing two nodes.
@ -1038,7 +1043,3 @@ class Node:
True if this node doesn't equal the other node, False if it does equal.
"""
return not self.__eq__(other)
# Nasty hack to get around mypy's lack of scoping
float = __float
bool = __bool

View File

@ -1,6 +1,6 @@
import binascii
import hashlib
from typing import Optional
from typing import Final, Optional
from bemani.protocol.lz77 import Lz77
from bemani.protocol.binary import BinaryEncoding
@ -18,16 +18,16 @@ class EAmuseProtocol:
"""
A wrapper object that encapsulates encoding/decoding the E-Amusement protocol by Konami.
"""
SHARED_SECRET = b'\x69\xD7\x46\x27\xD9\x85\xEE\x21\x87\x16\x15\x70\xD0\x8D\x93\xB1\x24\x55\x03\x5B\x6D\xF0\xD8\x20\x5D\xF5'
SHARED_SECRET: Final[bytes] = b'\x69\xD7\x46\x27\xD9\x85\xEE\x21\x87\x16\x15\x70\xD0\x8D\x93\xB1\x24\x55\x03\x5B\x6D\xF0\xD8\x20\x5D\xF5'
XML = 1
BINARY = 2
XML: Final[int] = 1
BINARY: Final[int] = 2
SHIFT_JIS_LEGACY = "shift-jis-legacy"
SHIFT_JIS = "shift-jis"
EUC_JP = "euc-jp"
UTF_8 = "utf-8"
ASCII = "ascii"
SHIFT_JIS_LEGACY: Final[str] = "shift-jis-legacy"
SHIFT_JIS: Final[str] = "shift-jis"
EUC_JP: Final[str] = "euc-jp"
UTF_8: Final[str] = "utf-8"
ASCII: Final[str] = "ascii"
def __init__(self) -> None:
"""
@ -36,7 +36,7 @@ class EAmuseProtocol:
self.last_text_encoding: Optional[str] = None
self.last_packet_encoding: Optional[int] = None
def __rc4_crypt(self, data: bytes, key: bytes) -> bytes:
def _rc4_crypt(self, data: bytes, key: bytes) -> bytes:
"""
Given a data blob and a key blob, perform RC4 encryption/decryption.
@ -95,7 +95,7 @@ class EAmuseProtocol:
if key:
# This is an encrypted old-style packet
return self.__rc4_crypt(data, key)
return self._rc4_crypt(data, key)
# No encryption
return data

View File

@ -1,6 +1,6 @@
import copy
import struct
from typing import Any, Dict, Iterator, List, Optional, Tuple
from typing import Any, Dict, Final, Iterator, List, Optional, Tuple
from bemani.protocol.stream import InputStream
from bemani.protocol.node import Node
@ -468,7 +468,7 @@ class XmlEncoding:
# The string values should match the constants in EAmuseProtocol.
# I have no better way to link these than to write this comment,
# as otherwise we would have a circular dependency.
ACCEPTED_ENCODINGS = ['shift-jis', 'euc-jp', 'utf-8', 'ascii']
ACCEPTED_ENCODINGS: Final[List[str]] = ['shift-jis', 'euc-jp', 'utf-8', 'ascii']
def __init__(self) -> None:
"""

View File

@ -1,4 +1,5 @@
# vim: set fileencoding=utf-8
import os
import sys
import unittest
from typing import Container, List, Dict, Any
@ -32,3 +33,9 @@ class FakeCursor():
def fetchall(self) -> List[Dict[str, Any]]:
return self.__rows
def get_fixture(name: str) -> bytes:
location = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(location, name), "rb") as fp:
return fp.read()

View File

@ -8,14 +8,14 @@ class TestAESCipher(unittest.TestCase):
def test_pad(self) -> None:
aes = AESCipher('a wonderful key')
self.assertEqual(aes._AESCipher__pad(''), '0.--------------')
self.assertEqual(aes._AESCipher__unpad(aes._AESCipher__pad('')), '')
self.assertEqual(aes._AESCipher__pad('1337'), '4.1337----------')
self.assertEqual(aes._AESCipher__unpad(aes._AESCipher__pad('1337')), '1337')
self.assertEqual(aes._AESCipher__pad('aaaaaaaaaaaaaaaa'), '16.aaaaaaaaaaaaaaaa-------------')
self.assertEqual(aes._AESCipher__unpad(aes._AESCipher__pad('aaaaaaaaaaaaaaaa')), 'aaaaaaaaaaaaaaaa')
self.assertEqual(aes._AESCipher__pad('aaaaaaaaaaaaa'), '13.aaaaaaaaaaaaa')
self.assertEqual(aes._AESCipher__unpad(aes._AESCipher__pad('aaaaaaaaaaaaa')), 'aaaaaaaaaaaaa')
self.assertEqual(aes._pad(''), '0.--------------')
self.assertEqual(aes._unpad(aes._pad('')), '')
self.assertEqual(aes._pad('1337'), '4.1337----------')
self.assertEqual(aes._unpad(aes._pad('1337')), '1337')
self.assertEqual(aes._pad('aaaaaaaaaaaaaaaa'), '16.aaaaaaaaaaaaaaaa-------------')
self.assertEqual(aes._unpad(aes._pad('aaaaaaaaaaaaaaaa')), 'aaaaaaaaaaaaaaaa')
self.assertEqual(aes._pad('aaaaaaaaaaaaa'), '13.aaaaaaaaaaaaa')
self.assertEqual(aes._unpad(aes._pad('aaaaaaaaaaaaa')), 'aaaaaaaaaaaaa')
def test_crypto(self) -> None:
aes = AESCipher('a wonderful key')

View File

@ -15,9 +15,9 @@ class TestCardCipher(unittest.TestCase):
for pair in test_ciphers:
inp = bytes(pair[0])
out = bytes(pair[1])
encoded = CardCipher._CardCipher__encode(inp) # type: ignore
encoded = CardCipher._encode(inp) # type: ignore
self.assertEqual(encoded, out, f"Card encode {encoded} doesn't match expected {out}")
decoded = CardCipher._CardCipher__decode(out) # type: ignore
decoded = CardCipher._decode(out) # type: ignore
self.assertEqual(decoded, inp, f"Card decode {decoded} doesn't match expected {inp}")
def test_external_cipher(self) -> None:

View File

@ -13,10 +13,10 @@ class TestRC4Cipher(unittest.TestCase):
encrypted = b'\x04]Q\x11\x0cw\x7fO\xfa\x03\xa3\xdf\xb6\x02\xb7d\x9f\x13U\x19\xc9-j\x96\x15yl\x98\xee_<\xfa\x9b\x8f\xbe}\xf4\x05l5\x0e\xd6'
proto = EAmuseProtocol()
cyphertext = proto._EAmuseProtocol__rc4_crypt(data, key)
cyphertext = proto._rc4_crypt(data, key)
self.assertEqual(encrypted, cyphertext)
plaintext = proto._EAmuseProtocol__rc4_crypt(cyphertext, key)
plaintext = proto._rc4_crypt(cyphertext, key)
self.assertEqual(data, plaintext)
def test_small_data_random(self) -> None:
@ -24,10 +24,10 @@ class TestRC4Cipher(unittest.TestCase):
key = bytes([random.randint(0, 255) for _ in range(16)])
proto = EAmuseProtocol()
cyphertext = proto._EAmuseProtocol__rc4_crypt(data, key)
cyphertext = proto._rc4_crypt(data, key)
self.assertNotEqual(data, cyphertext)
plaintext = proto._EAmuseProtocol__rc4_crypt(cyphertext, key)
plaintext = proto._rc4_crypt(cyphertext, key)
self.assertEqual(data, plaintext)
def test_large_data_random(self) -> None:
@ -35,8 +35,8 @@ class TestRC4Cipher(unittest.TestCase):
key = bytes([random.randint(0, 255) for _ in range(16)])
proto = EAmuseProtocol()
cyphertext = proto._EAmuseProtocol__rc4_crypt(data, key)
cyphertext = proto._rc4_crypt(data, key)
self.assertNotEqual(data, cyphertext)
plaintext = proto._EAmuseProtocol__rc4_crypt(cyphertext, key)
plaintext = proto._rc4_crypt(cyphertext, key)
self.assertEqual(data, plaintext)

View File

@ -4,6 +4,7 @@ import random
import unittest
from bemani.protocol.lz77 import Lz77, Lz77Decompress
from bemani.tests.helpers import get_fixture
class TestLZ77Decompressor(unittest.TestCase):
@ -16,22 +17,16 @@ class TestLZ77Decompressor(unittest.TestCase):
# Save our ring position, write a chunk of data
readpos = dec.write_pos
dec._Lz77Decompress__ring_write(data)
dec._ring_write(data)
# Read a chunk of data back from that buffer, see its the same
newdata = b''.join(dec._Lz77Decompress__ring_read(readpos, amount))
newdata = b''.join(dec._ring_read(readpos, amount))
self.assertEqual(data, newdata)
# Verify integrity of ringbuffer
self.assertEqual(len(dec.ring), Lz77Decompress.RING_LENGTH)
def get_fixture(name: str) -> bytes:
location = os.path.dirname(os.path.abspath(__file__))
with open(os.path.join(location, name), "rb") as fp:
return fp.read()
class TestLz77RealCompressor(unittest.TestCase):
def test_small_data_random(self) -> None:
lz77 = Lz77()