666 lines
18 KiB
Python
666 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()
|