626 lines
18 KiB
Python
626 lines
18 KiB
Python
import argparse
|
|
import sys
|
|
from typing import Any, Dict, Optional
|
|
import yaml
|
|
|
|
from bemani.client import ClientProtocol, BaseClient
|
|
from bemani.client.iidx import (
|
|
IIDXTricoroClient,
|
|
IIDXSpadaClient,
|
|
IIDXPendualClient,
|
|
IIDXCopulaClient,
|
|
IIDXSinobuzClient,
|
|
IIDXCannonBallersClient,
|
|
IIDXRootageClient,
|
|
)
|
|
from bemani.client.jubeat import (
|
|
JubeatSaucerClient,
|
|
JubeatSaucerFulfillClient,
|
|
JubeatPropClient,
|
|
JubeatQubellClient,
|
|
JubeatClanClient,
|
|
JubeatFestoClient,
|
|
)
|
|
from bemani.client.popn import (
|
|
PopnMusicTuneStreetClient,
|
|
PopnMusicFantasiaClient,
|
|
PopnMusicSunnyParkClient,
|
|
PopnMusicLapistoriaClient,
|
|
PopnMusicEclaleClient,
|
|
PopnMusicUsaNekoClient,
|
|
PopnMusicPeaceClient,
|
|
PopnMusicKaimeiClient,
|
|
)
|
|
from bemani.client.ddr import (
|
|
DDRX2Client,
|
|
DDRX3Client,
|
|
DDR2013Client,
|
|
DDR2014Client,
|
|
DDRAceClient,
|
|
)
|
|
from bemani.client.sdvx import (
|
|
SoundVoltexBoothClient,
|
|
SoundVoltexInfiniteInfectionClient,
|
|
SoundVoltexGravityWarsS1Client,
|
|
SoundVoltexGravityWarsS2Client,
|
|
SoundVoltexHeavenlyHavenClient,
|
|
)
|
|
from bemani.client.museca import (
|
|
Museca1Client,
|
|
Museca1PlusClient,
|
|
)
|
|
from bemani.client.reflec import (
|
|
ReflecBeat,
|
|
ReflecBeatLimelight,
|
|
ReflecBeatColette,
|
|
ReflecBeatGroovinUpper,
|
|
ReflecBeatVolzza,
|
|
ReflecBeatVolzza2,
|
|
)
|
|
from bemani.client.bishi import TheStarBishiBashiClient
|
|
from bemani.client.mga.mga import MetalGearArcadeClient
|
|
|
|
|
|
def get_client(proto: ClientProtocol, pcbid: str, game: str, config: Dict[str, Any]) -> BaseClient:
|
|
if game == 'pnm-tune-street':
|
|
return PopnMusicTuneStreetClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-fantasia':
|
|
return PopnMusicFantasiaClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-sunny-park':
|
|
return PopnMusicSunnyParkClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-lapistoria':
|
|
return PopnMusicLapistoriaClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-eclale':
|
|
return PopnMusicEclaleClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-usaneko':
|
|
return PopnMusicUsaNekoClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-peace':
|
|
return PopnMusicPeaceClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'pnm-kaimei':
|
|
return PopnMusicKaimeiClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-saucer':
|
|
return JubeatSaucerClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-saucer-fulfill':
|
|
return JubeatSaucerFulfillClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-prop':
|
|
return JubeatPropClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-qubell':
|
|
return JubeatQubellClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-clan':
|
|
return JubeatClanClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'jubeat-festo':
|
|
return JubeatFestoClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-rootage':
|
|
return IIDXRootageClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-cannonballers':
|
|
return IIDXCannonBallersClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-sinobuz':
|
|
return IIDXSinobuzClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-copula':
|
|
return IIDXCopulaClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-pendual':
|
|
return IIDXPendualClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-spada':
|
|
return IIDXSpadaClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'iidx-tricoro':
|
|
return IIDXTricoroClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'bishi':
|
|
return TheStarBishiBashiClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'ddr-x2':
|
|
return DDRX2Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'ddr-x3':
|
|
return DDRX3Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'ddr-2013':
|
|
return DDR2013Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'ddr-2014':
|
|
return DDR2014Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'ddr-ace':
|
|
return DDRAceClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'sdvx-booth':
|
|
return SoundVoltexBoothClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'sdvx-infinite-infection':
|
|
return SoundVoltexInfiniteInfectionClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'sdvx-gravity-wars-s1':
|
|
return SoundVoltexGravityWarsS1Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'sdvx-gravity-wars-s2':
|
|
return SoundVoltexGravityWarsS2Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'sdvx-heavenly-haven':
|
|
return SoundVoltexHeavenlyHavenClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'museca-1':
|
|
return Museca1Client(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'museca-1+1/2':
|
|
return Museca1PlusClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec':
|
|
return ReflecBeat(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec-limelight':
|
|
return ReflecBeatLimelight(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec-colette':
|
|
return ReflecBeatColette(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec-groovin-upper':
|
|
return ReflecBeatGroovinUpper(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec-volzza':
|
|
return ReflecBeatVolzza(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'reflec-volzza2':
|
|
return ReflecBeatVolzza2(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
if game == 'metal-gear-arcade':
|
|
return MetalGearArcadeClient(
|
|
proto,
|
|
pcbid,
|
|
config,
|
|
)
|
|
|
|
raise Exception(f'Unknown game {game}')
|
|
|
|
|
|
def mainloop(address: str, port: int, configfile: str, action: str, game: str, cardid: Optional[str], verbose: bool) -> None:
|
|
games = {
|
|
'pnm-tune-street': {
|
|
'name': "Pop'n Music Tune Street",
|
|
'model': "K39:J:B:A:2010122200",
|
|
'old_profile_model': "J39:J:A:A",
|
|
'avs': None,
|
|
},
|
|
'pnm-fantasia': {
|
|
'name': "Pop'n Music Fantasia",
|
|
'model': "L39:J:B:A:2012091900",
|
|
'old_profile_model': "K39:J:A:A",
|
|
'avs': "2.13.6 r4921",
|
|
},
|
|
'pnm-sunny-park': {
|
|
'name': "Pop'n Music Sunny Park",
|
|
'model': "M39:J:B:A:2014061900",
|
|
'old_profile_model': "L39:J:A:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'pnm-lapistoria': {
|
|
'name': "Pop'n Music Lapistoria",
|
|
'model': "M39:J:B:A:2015081900",
|
|
'old_profile_model': "M39:J:B:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'pnm-eclale': {
|
|
'name': "Pop'n Music Eclale",
|
|
'model': "M39:J:B:A:2016100500",
|
|
'old_profile_model': "M39:J:B:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'pnm-usaneko': {
|
|
'name': "Pop'n Music Usagi to Neko to Shounen no Yume",
|
|
'model': "M39:J:B:A:2018101500",
|
|
'old_profile_model': "M39:J:B:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'pnm-peace': {
|
|
'name': "Pop'n Music peace",
|
|
'model': "M39:J:B:A:2020092800",
|
|
'old_profile_model': "M39:J:B:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'pnm-kaimei': {
|
|
'name': "Pop'n Music Kaimei riddles",
|
|
'model': "M39:J:B:A:2022061300",
|
|
'old_profile_model': "M39:J:B:A",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'jubeat-saucer': {
|
|
'name': "Jubeat Saucer",
|
|
'model': "L44:J:A:A:2014012802",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'jubeat-saucer-fulfill': {
|
|
'name': "Jubeat Saucer Fulfill",
|
|
'model': "L44:J:B:A:2014111800",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'jubeat-prop': {
|
|
'name': "Jubeat Prop",
|
|
'model': "L44:J:B:A:2016031700",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'jubeat-qubell': {
|
|
'name': "Jubeat Qubell",
|
|
'model': "L44:J:D:A:2016111400",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'jubeat-clan': {
|
|
'name': "Jubeat Clan",
|
|
'model': "L44:J:E:A:2018070901",
|
|
'avs': "2.17.3 r8311",
|
|
},
|
|
'jubeat-festo': {
|
|
'name': "Jubeat Festo",
|
|
'model': "L44:J:B:A:2022052400",
|
|
'avs': "2.17.3 r8311",
|
|
},
|
|
'iidx-rootage': {
|
|
'name': "Beatmania IIDX ROOTAGE",
|
|
'model': "LDJ:J:A:A:2019090200",
|
|
'avs': "2.17.0 r7883"
|
|
},
|
|
'iidx-cannonballers': {
|
|
'name': "Beatmania IIDX CANNON BALLERS",
|
|
'model': "LDJ:J:A:A:2018091900",
|
|
'avs': "2.17.0 r7883"
|
|
},
|
|
'iidx-sinobuz': {
|
|
'name': "Beatmania IIDX SINOBUZ",
|
|
'model': "LDJ:J:A:A:2017082800",
|
|
'avs': "2.16.1 r6901",
|
|
},
|
|
'iidx-copula': {
|
|
'name': "Beatmania IIDX copula",
|
|
'model': "LDJ:J:A:A:2016083100",
|
|
'avs': "2.16.1 r6901",
|
|
},
|
|
'iidx-pendual': {
|
|
'name': "Beatmania IIDX PENDUAL",
|
|
'model': "LDJ:A:A:A:2015080500",
|
|
'avs': "2.16.1 r6901",
|
|
},
|
|
'iidx-spada': {
|
|
'name': "Beatmania IIDX SPADA",
|
|
'model': "LDJ:A:A:A:2014071600",
|
|
'avs': "2.16.1 r6901",
|
|
},
|
|
'iidx-tricoro': {
|
|
'name': "Beatmania IIDX Tricoro",
|
|
'model': "LDJ:J:A:A:2013090900",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'bishi': {
|
|
'name': "The★BishiBashi",
|
|
'model': "IBB:A:A:A:2009092900",
|
|
'avs': None,
|
|
},
|
|
'ddr-x2': {
|
|
'name': "DanceDanceRevolution X2",
|
|
'model': "JDX:J:A:A:2010111000",
|
|
'avs': None,
|
|
},
|
|
'ddr-x3': {
|
|
'name': "DanceDanceRevolution X3 VS 2ndMIX",
|
|
'model': "KDX:J:A:A:2012112600",
|
|
'avs': "2.13.6 r4921",
|
|
},
|
|
'ddr-2013': {
|
|
'name': "DanceDanceRevolution (2013)",
|
|
'model': "MDX:J:A:A:2014032700",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'ddr-2014': {
|
|
'name': "DanceDanceRevolution (2014)",
|
|
'model': "MDX:A:A:A:2015122100",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'ddr-ace': {
|
|
'name': "DanceDanceRevolution A",
|
|
'model': "MDX:U:D:A:2017121400",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'sdvx-booth': {
|
|
'name': "SOUND VOLTEX BOOTH",
|
|
'model': "KFC:J:A:A:2013052900",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'sdvx-infinite-infection': {
|
|
'name': "SOUND VOLTEX II -infinite infection-",
|
|
'model': "KFC:J:A:A:2014102200",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'sdvx-gravity-wars-s1': {
|
|
'name': "SOUND VOLTEX III GRAVITY WARS Season 1",
|
|
'model': "KFC:J:A:A:2015111602",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'sdvx-gravity-wars-s2': {
|
|
'name': "SOUND VOLTEX III GRAVITY WARS Season 2",
|
|
'model': "KFC:J:A:A:2016121900",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'sdvx-heavenly-haven': {
|
|
'name': "SOUND VOLTEX IV HEAVENLY HAVEN",
|
|
'model': "KFC:J:A:A:2019020600",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'museca-1': {
|
|
'name': "MÚSECA",
|
|
'model': "PIX:J:A:A:2016071300",
|
|
'avs': "2.17.0 r7883",
|
|
},
|
|
'museca-1+1/2': {
|
|
'name': "MÚSECA 1+1/2",
|
|
'model': "PIX:J:A:A:2017042600",
|
|
'avs': "2.17.0 r7883",
|
|
},
|
|
'reflec': {
|
|
'name': "REFLEC BEAT",
|
|
'model': "KBR:A:A:A:2011112300",
|
|
'avs': None,
|
|
},
|
|
'reflec-limelight': {
|
|
'name': "REFLEC BEAT limelight",
|
|
'model': "LBR:A:A:A:2012082900",
|
|
'avs': "2.13.6 r4921",
|
|
},
|
|
'reflec-colette': {
|
|
'name': "REFLEC BEAT colette",
|
|
'model': "MBR:J:A:A:2014011600",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'reflec-groovin-upper': {
|
|
'name': "REFLEC BEAT groovin'!! Upper",
|
|
'model': "MBR:J:A:A:2015102100",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'reflec-volzza': {
|
|
'name': "REFLEC BEAT VOLZZA",
|
|
'model': "MBR:J:A:A:2016030200",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'reflec-volzza2': {
|
|
'name': "REFLEC BEAT VOLZZA 2",
|
|
'model': "MBR:J:A:A:2016100400",
|
|
'avs': "2.15.8 r6631",
|
|
},
|
|
'metal-gear-arcade': {
|
|
'name': "Metal Gear Arcade",
|
|
'model': "I36:J:A:A:2011092900",
|
|
'avs': None,
|
|
},
|
|
}
|
|
if action == 'list':
|
|
for game in sorted([game for game in games]):
|
|
print(f'{game} - {games[game]["name"]}')
|
|
sys.exit(0)
|
|
if action == 'game':
|
|
if game not in games:
|
|
print(f'Unknown game {game}')
|
|
sys.exit(2)
|
|
|
|
config = yaml.safe_load(open(configfile))
|
|
|
|
print(f'Emulating {games[game]["name"]}')
|
|
emu = get_client(
|
|
ClientProtocol(
|
|
address,
|
|
port,
|
|
config['core']['encryption'],
|
|
config['core']['compression'],
|
|
verbose,
|
|
),
|
|
config['core']['pcbid'],
|
|
game,
|
|
games[game],
|
|
)
|
|
|
|
emu.verify(cardid)
|
|
|
|
|
|
def main() -> None:
|
|
parser = argparse.ArgumentParser(description="A utility to generate game-like traffic for testing an eAmusement server.")
|
|
parser.add_argument("-p", "--port", help="Port to talk to. Defaults to 80", type=int, default=80)
|
|
parser.add_argument("-a", "--address", help="Address to talk to. Defaults to 127.0.0.1", type=str, default="127.0.0.1")
|
|
parser.add_argument("-c", "--config", help="Core configuration. Defaults to trafficgen.yaml", type=str, default="trafficgen.yaml")
|
|
parser.add_argument("-g", "--game", help="The game that should be emulated. Should be one of the games returned by --list", type=str, default=None)
|
|
parser.add_argument("-l", "--list", help="List all known games and exit.", action="store_true")
|
|
parser.add_argument("-i", "--cardid", help="Use this card ID instead of a random one.", type=str, default=None)
|
|
parser.add_argument("-v", "--verbose", help="Print packets that are sent/received.", action='store_true', default=False)
|
|
args = parser.parse_args()
|
|
|
|
if args.list:
|
|
action = 'list'
|
|
game = None
|
|
elif args.game:
|
|
action = 'game'
|
|
game = args.game
|
|
else:
|
|
print("Unknown action to perform. Please specify --game <game> or --list")
|
|
sys.exit(1)
|
|
|
|
game = {
|
|
'pnm-19': 'pnm-tune-street',
|
|
'pnm-20': 'pnm-fantasia',
|
|
'pnm-21': 'pnm-sunny-park',
|
|
'pnm-22': 'pnm-lapistoria',
|
|
'pnm-23': 'pnm-eclale',
|
|
'pnm-24': 'pnm-usaneko',
|
|
'pnm-25': 'pnm-peace',
|
|
'pnm-26': 'pnm-kaimei',
|
|
'iidx-20': 'iidx-tricoro',
|
|
'iidx-21': 'iidx-spada',
|
|
'iidx-22': 'iidx-pendual',
|
|
'iidx-23': 'iidx-copula',
|
|
'iidx-24': 'iidx-sinobuz',
|
|
'iidx-25': 'iidx-cannonballers',
|
|
'iidx-26': 'iidx-rootage',
|
|
'jubeat-5': 'jubeat-saucer',
|
|
'jubeat-5+': 'jubeat-saucer-fulfill',
|
|
'jubeat-6': 'jubeat-prop',
|
|
'jubeat-7': 'jubeat-qubell',
|
|
'jubeat-8': 'jubeat-clan',
|
|
'jubeat-9': 'jubeat-festo',
|
|
'ddr-12': 'ddr-x2',
|
|
'ddr-13': 'ddr-x3',
|
|
'ddr-14': 'ddr-2013',
|
|
'ddr-15': 'ddr-2014',
|
|
'ddr-16': 'ddr-ace',
|
|
'sdvx-1': 'sdvx-booth',
|
|
'sdvx-2': 'sdvx-infinite-infection',
|
|
'sdvx-3s1': 'sdvx-gravity-wars-s1',
|
|
'sdvx-3s2': 'sdvx-gravity-wars-s2',
|
|
'sdvx-4': 'sdvx-heavenly-haven',
|
|
'reflec-1': 'reflec',
|
|
'reflec-2': 'reflec-limelight',
|
|
'reflec-3': 'reflec-colette',
|
|
'reflec-4': 'reflec-groovin-upper',
|
|
'reflec-5': 'reflec-volzza',
|
|
'reflec-6': 'reflec-volzza2',
|
|
'mga': 'metal-gear-arcade',
|
|
}.get(game, game)
|
|
|
|
mainloop(args.address, args.port, args.config, action, game, args.cardid, args.verbose)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|