diff --git a/images/eventlog_ghidra.png b/images/eventlog_ghidra.png new file mode 100644 index 0000000..ed7069c Binary files /dev/null and b/images/eventlog_ghidra.png differ diff --git a/images/eventlog_ida.png b/images/eventlog_ida.png new file mode 100644 index 0000000..031fa09 Binary files /dev/null and b/images/eventlog_ida.png differ diff --git a/images/matching_request_ghidra.png b/images/matching_request_ghidra.png new file mode 100644 index 0000000..75fa5e5 Binary files /dev/null and b/images/matching_request_ghidra.png differ diff --git a/images/matching_request_ida.png b/images/matching_request_ida.png new file mode 100644 index 0000000..ab33ab0 Binary files /dev/null and b/images/matching_request_ida.png differ diff --git a/index.html b/index.html index bb63195..e7fba18 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ -
Why?
I was curious how these APIs work, yet could find little to nothing on Google. There are a number of closed-source projects, with presumably similarly closed-source internal documentation, and a scattering of implementations of things, yet I couldn't find a site that actually just documents how the API works. If I'm going to have to reverse engineer an open source project (or a closed source one, for that matter), I might as well just go reverse engineer an actual game (or it's stdlib, as most of my time has been spent currently).
+For the sake of being lazy, I'll probably end up calling it eAmuse more than anything else throughout these
+ pages. Other names you may come across include httac
and xrpc
. The latter are the
+ suite of HTTP functions used in the Bemani stdlib, and the name of their communication protocol they implement
+ at the application layer, but whenever someone refers to any of them in the context of a rhythm game, they will
+ be referring to the things documented here.
These pages are very much a work in progress, and are being written as I reverse engineer parts of the protocol. I've been asserting all my assumptions by writing my own implementation as I go, however it currently isn't sharable quality code and, more importantly, the purpose of these pages is to make implementation of one's - own code hopefully trivial.
+ own code hopefully trivial (teach a man to fish, and all that).Sharing annotated sources for all of the games' stdlibs would be both impractical and unwise. Where relevant however I try to include snippets to illustrate concepts, and have included their locations in the source for if you feel like taking a dive too.
diff --git a/packet.html b/packet.html index 1c6fba0..370d5f4 100644 --- a/packet.html +++ b/packet.html @@ -63,7 +63,7 @@ can be a little confusing, remembering that this is encoding an XML tree can make it easier to parse.To start with, let's take a look at the overall structure of the packets.
-0 | @@ -123,19 +123,19 @@||
0x42 | +0x42 |
Compressed data |
0x43 | +0x43 |
Compressed, no data |
0x45 | +0x45 |
Decompressed data |
0x46 | +0x46 |
Decompressed, no data |
E | -~E | +E |
+ ~E |
Encoding name | |||||
0x20 | -0xDF | -ASCII | +0x20 |
+ 0xDF |
+ ASCII |
||||
0x40 | -0xBF | -ISO-8859-1 | -ISO_8859-1 | +0xBF |
+ ISO-8859-1 |
+ ISO_8859-1 |
+ |||
0x60 | -0x9F | -EUC-JP | -EUCJP | -EUC_JP | +0x60 |
+ 0x9F |
+ EUC-JP |
+ EUCJP |
+ EUC_JP |
0x80 | -0x7F | -SHIFT-JIS | -SHIFT_JIS | -SJIS | +0x80 |
+ 0x7F |
+ SHIFT-JIS |
+ SHIFT_JIS |
+ SJIS |
0xA0 | -0x5F | -UTF-8 | -UTF8 | +0xA0 |
+ 0x5F |
+ UTF-8 |
+ UTF8 |
A tag definition looks like:
-0 | @@ -264,7 +264,7 @@ used later when unpacking the main data, so we need not worry about it for now, but be warned it exists and is possibly the least fun part of this format. -
ID | @@ -815,7 +815,7 @@ for our bucket.
0 | diff --git a/protocol.html b/protocol.html index c52a178..ed28946 100644 --- a/protocol.html +++ b/protocol.html @@ -57,13 +57,72 @@ </response>
Status | +Meaning | +
0 |
+ Success | +
109 |
+ No profile | +
110 |
+ Not allowed | +
112 |
+ Card not found (cardmng.inquire ) |
+
116 |
+ Card pin invalid (cardmng.authpass ) |
+
Turns out bemani have been quite sensible in how they implemented their code for creating structures, so it's + rather readable. That said, if you've been using Ghidra (like me!), this is the time to switch to IDA. I'll + let the below screenshots below speak for themselves: +
+ +I know which of these I'd rather use for reverse engineering (sorry, Ghidra)!
+eventlog.%s
eacoin.getecstatus
eacoin.touch
eacoin.opchpass
eacoin.opchecking
eacoin.opcheckin
eacoin.opcheckout
eacoin.getlog
G_GAMED
S_ERROR
S_PWRON
TODO: find more!T_OTDEMO
<call ...>
<matching method="request">
- placeholder
+ <matchtyp __type="s32" />
+ <matchgrp __type="s32" />
+ <matchflg __type="s32" />
+ <waituser __type="s32" />
+ <waittime __type="s32" />
+
+ <joinip __type="str" />
+ <localip __type="str" />
+ <localport __type="s32" />
+ <dataid __type="str" />
+ <gamekind __type="str" />
+ <locationid __type="str" />
+ <lineid __type="str" />
+ <locationcountry __type="str" />
+ <locationregion __type="str" />
+
</matching>
</call>
cardmng.inquire
<call ...>
- <cardmng method="inquire" cardid="" cardtype="" update="" />
+ <cardmng method="inquire" cardid="" cardtype="" update="" model*="" />
+</call>
+ <response>
+ <cardmng status="status" refid="" dataid="" pcode="" newflag="" binded="" expired=" ecflag="" useridflag="" extidflag="" lastupdate="" />
+</response>
+ If the cardid
cannot be found, status
should be set to 112
with no other
+ information return. Otherwise, we return information about the found card.
refid |
+ A reference to this card to be used in other requests | +
dataid |
+ Appears to be set the same as refid ; presumably to allow different keys for game state vs
+ login details. |
+
newflag |
+ 1 or 0 |
+
binded |
+ Has a profile ever been created for this game (or an older version, requiring a migration)
+ (1 or 0 ) |
+
expired |
+ Did we find | +
cardmng.getrefid
<call ...>
+ <cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" />
+</call>
+ <response>
+ <cardmng status="status" refid="" dataid="" pcode="" />
+</response>
+
+ cardmng.bindmodel
<call ...>
+ <cardmng method="bindmodel" refid="" newflag="" model*="" />
+</call>
+ <response>
+ <cardmng status="status" dataid="" />
+</response>
+
+ cardmng.bindcard
<call ...>
+ <cardmng method="bindcard" cardtype="" newid="" refid="" model*="" />
</call>
<response>
<cardmng status="status" />
</response>
- cardmng.getrefid
<call ...>
- <cardmng method="getrefid">
- placeholder
- </cardmng>
-</call>
- <response>
- <cardmng status="status">
- placeholder
- </cardmng>
-</response>
-
- cardmng.bindmodel
<call ...>
- <cardmng method="bindmodel">
- placeholder
- </cardmng>
-</call>
- <response>
- <cardmng status="status">
- placeholder
- </cardmng>
-</response>
-
- cardmng.bindcard
<call ...>
- <cardmng method="bindcard">
- placeholder
- </cardmng>
-</call>
- <response>
- <cardmng status="status">
- placeholder
- </cardmng>
-</response>
-
cardmng.authpass
<call ...>
- <cardmng method="authpass">
- placeholder
- </cardmng>
+ <cardmng method="authpass" refid="" pass="" model*="" />
</call>
<response>
- <cardmng status="status">
- placeholder
- </cardmng>
+ <cardmng status="status" />
</response>
cardmng.getkeepspan
<call ...>
- <cardmng method="getkeepspan">
- placeholder
- </cardmng>
+ <cardmng method="getkeepspan" model*="" />
</call>
<response>
- <cardmng status="status">
- placeholder
- </cardmng>
+ <cardmng status="status" keepspan="" />
</response>
cardmng.getkeepremain
<call ...>
- <cardmng method="getkeepremain">
- placeholder
- </cardmng>
+ <cardmng method="getkeepremain" refid="" model*="" />
</call>
<response>
- <cardmng status="status">
- placeholder
- </cardmng>
+ <cardmng status="status" keepremain="" />
</response>
cardmng.getdatalist
<call ...>
- <cardmng method="getdatalist">
- placeholder
- </cardmng>
+ <cardmng method="getdatalist" refid="" model*="" />
</call>
<response>
<cardmng status="status">
- placeholder
+ <item[]>
+ <mcode __type="str" />
+ <dataid __type="str" />
+ <regtime __type="str" />
+ <lasttime __type="str" />
+ <exptime __type="str" />
+ <expflag __type="u8" />
+ </item>
</cardmng>
</response>
@@ -587,32 +669,29 @@
package
package.list
<call ...>
- <package method="list">
- placeholder
- </package>
+ <package method="list" pkgtype="pkgtype" model*="" />
</call>
+ all
is the only currently observed value for pkgtype
<response>
<package status="status">
- placeholder
+ <item[] url="" />
</package>
</response>
+ A list of all packages available for download.
package.intend
<call ...>
- <package method="intend">
- placeholder
- </package>
+ <package method="intend" url="" model*="" />
</call>
<response>
- <package status="status">
- placeholder
- </package>
+ <package status="status" />
</response>
@@ -620,29 +699,26 @@
userdata.read
<call ...>
- <userdata method="read">
- placeholder
- </userdata>
+ <userdata method="read" card*="" model*="" label="" />
</call>
<response>
- <userdata status="status">
- placeholder
+ <userdata status="status" time="">
+ <b[] __type="" />
</userdata>
</response>
+ __type
here can be either bin
or str
userdata.write
<call ...>
- <userdata method="write">
- placeholder
+ <userdata method="write" card="" time="" model*="" label*="" >
+ <b[] __type="str" />
</userdata>
</call>
<response>
- <userdata status="status">
- placeholder
- </userdata>
+ <userdata status="status" />
</response>
@@ -650,7 +726,7 @@
services.get
<call ...>
- <services method="get">
+ <services method="get" model*="" >
<info>
<AVS2 __type="str">AVS2 version</AVS2>
</info>
@@ -659,7 +735,7 @@
Response:
<response>
<services expire="" method="get" mode="" status="status">
- <item name="service" url="url" />
+ <item[] name="service" url="url" />
</services>
</response>
Known services are:
@@ -687,19 +763,30 @@
sidmgr
globby
- Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). ntp
is a notable exception, unless you're planning on reimplementing NTP. keepalive
will likely alsop be a custom URL with query parameters pre-baked.
+ Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). ntp
is a
+ notable exception, unless you're planning on reimplementing NTP. keepalive
will likely alsop be a
+ custom URL with query parameters pre-baked.
+ mode
is one of operation
, debug
, test
, or
+ factory
.
+
pcbtracker
pcbtracker.alive
Request:
<call ...>
- <pcbtracker method="alive" accountid="pcbid" ecflag="ecflag" hardid="hardware ID" softid="software ID" />
+ <pcbtracker method="alive" model*="" hardid="" softid="" accountid="" agree="" ecflag="" />
</call>
+ ecflag
here is determining if the arcade operator allows the use of paseli on this machine.
+ agree@
and ecflag@
appear to either be totally non present, or present with a value of
+ "1"
, but then again I may be reading the code wrong, so take that with a pinch of salt.
+
Response:
<response>
- <pcbtracker status="status" ecenable="" eclimit="" expire="" limit="" time="">
+ <pcbtracker status="" time="" limit="" ecenable="" eclimit="" >
</response>
+ As you might guess, ecenable@
is therefore the flag to determine if paseli is enabled (i.e. the
+ arcade operator and the server both allow its use).
pcbevent
@@ -707,14 +794,18 @@
Request:
<call ...>
<pcbevent method="put">
- placeholder
+ <time __type="time" />
+ <seq __type="u32" />
+ <item[]>
+ <name __type="str" />
+ <value __type="s32" />
+ <time __type="time" />
+ </item>
</pcbevent>
</call>
Response:
<response>
- <pcbevent status="status">
- placeholder
- </pcbevent>
+ <pcbevent status="status" />
</response>
@@ -722,12 +813,13 @@
message.get
Request:
<call ...>
- <message method="get" />
+ <message method="get" model*="" />
</call>
Response:
- TODO: Investigate this response more
<response>
- <message expire="" status="status" />
+ <message expire="" status="status">
+ <item[] name="" start="" end="" data="" />
+ </message>
</response>
@@ -735,31 +827,129 @@
facility.get
Request:
<call ...>
- <facility method="get">
- placeholder
- </facility>
+ <facility method="get" privateip*="" encoding*="" />
</call>
Response:
<response>
- <facility status="status">
- placeholder
+ <facility expire=""\ status="status">
+ <calendar*>
+ <year __type="s16" />
+ <holiday __type="s16" />
+ </calendar>
+ <location>
+ <id __type="str" />
+ <country __type="str" />
+ <region __type="str" />
+ <name __type="str" />
+ <type __type="u8" />
+ <countryname __type="str" />
+ <countryjname __type="str" />
+ <regionname __type="str" />
+ <regionjname __type="str" />
+ <customercode __type="str" />
+ <companycode __type="str" />
+ <latitude __type="s32" />
+ <longitude __type="s32" />
+ <accuracy __type="u8" />
+ </location>
+ <line>
+ <id __type="str" />
+ <class __type="u8" />
+ </line>
+ <portfw>
+ <globalip __type="ip4" />
+ <globalport __type="s16" />
+ <privateport __type="s16" />
+ </portfw>
+ <public>
+ <flag __type="u8" />1</ flag>
+ <name __type="str" />
+ <latitude __type="str">0<latitude>
+ <longitude __type="str">0<longitude>
+ </public>
+ <share>
+ <eapass*>
+ <valid __type="?" />
+ </eapass>
+ <eacoin>
+ <notchamount __type="s32" />
+ <notchcount __type="s32" />
+ <supplylimit __type="s32">100000<supplylimit>
+ </eacoin>
+ <url>
+ <eapass __type="str">www.ea-pass.konami.net<eapass>
+ <arcadefan __type="str">www.konami.jp/am<arcadefan>
+ <konaminetdx __type="str">http://am.573.jp<konaminetdx>
+ <konamiid __type="str">http://id.konami.jp<konamiid>
+ <eagate __type="str">http://eagate.573.jp<eagate>
+ </url>
+ </share>
</facility>
</response>
-
+ I'm not totally sure what type share/eapass/valid
is meant to be, but it's optional, so I'd
+ suggest just not bothering and leaving it out :).
+
+
+
+ Country
+ Code
+
+
+
+ Hong Kong
+ HK
+
+
+ Taiwan
+ TW
+
+
+ Korea
+ KR
+
+
+ USA
+ US
+
+
+ Thailand
+ TH
+
+
+ Indonesia
+ ID
+
+
+ Singapore
+ SG
+
+
+ Phillipines
+ PH
+
+
+ Macao
+ MO
+
+
+ Japan
+ JP
+
+
+ globalip
(and associated ports) shold be the IP:port of the cabinet.
+ region
is used for Japan, and has the value JP-[prefecture]
where prefecture ranges
+ from 1 through 47.
+ TODO: Compile the list of regions
apsmanager
apsmanager.getstat
Request:
<call ...>
- <apsmanager method="getstat">
- placeholder
- </apsmanager>
+ <apsmanager method="getstat" model*="" />
</call>
Response:
<response>
- <apsmanager status="status">
- placeholder
- </apsmanager>
+ <apsmanager status="status" />
</response>
@@ -768,70 +958,71 @@
Request:
<call ...>
<sidmgr method="create">
- placeholder
+ <cardtype __type="str" />
+ <cardid __type="str" />
+ <cardgid __type="str" />
+ <steal __type="u8" />
</sidmgr>
</call>
Response:
<response>
<sidmgr status="status">
- placeholder
+ <state __type="u32" />
+ <e_count __type="u8" />
+ <last __type="time" />
+ <locked __type="time" />
+ <sid __type="str" />
+ <cardid_status __type="u8" />
+ <refid __type="str" />
</sidmgr>
</response>
sidmgr.open
Request:
<call ...>
- <sidmgr method="open">
- placeholder
+ <sidmgr method="open" sid="" >
+ <pass __type="str" />
</sidmgr>
</call>
Response:
<response>
<sidmgr status="status">
- placeholder
+ <state __type="u32" />
+ <refid __type="str" />
+ <locked __type="time" />
</sidmgr>
</response>
sidmgr.touch
Request:
<call ...>
- <sidmgr method="touch">
- placeholder
- </sidmgr>
+ <sidmgr method="touch" sid="" />
</call>
Response:
<response>
- <sidmgr status="status">
- placeholder
- </sidmgr>
+ <sidmgr status="status" />
</response>
sidmgr.branch
Request:
<call ...>
- <sidmgr method="branch">
- placeholder
- </sidmgr>
+ <sidmgr method="branch" sid="" />
</call>
Response:
<response>
- <sidmgr status="status">
- placeholder
- </sidmgr>
+ <sidmgr status="status" />
</response>
sidmgr.close
Request:
<call ...>
- <sidmgr method="close">
- placeholder
+ <sidmgr method="close" sid="" />
+ <cause __type="u32" />
</sidmgr>
</call>
Response:
<response>
- <sidmgr status="status">
- placeholder
- </sidmgr>
+ <sidmgr status="status" />
</response>
@@ -840,13 +1031,18 @@
Request:
<call ...>
<dlstatus method="done">
- placeholder
+ <url>
+ <param __type="str" />
+ </url>
+ <name __type="str" />
+ <size __type="s32" />
</dlstatus>
</call>
+
Response:
<response>
<dlstatus status="status">
- placeholder
+ <progress __type="s32" />
</dlstatus>
</response>
@@ -870,13 +1066,21 @@
Request:
<call ...>
<eacoin method="checkin">
- placeholder
+ <cardtype __type="str" />
+ <cardid __type="str" />
+ <passwd __type="str" />
+ <ectype __type="str" />
</eacoin>
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <sequence __type="s16" />
+ <acstatus __type="u8" />
+ <acid __type="str" />
+ <acname __type="str" />
+ <balance __type="s32" />
+ <sessid __type="str" />
</eacoin>
</response>
@@ -884,27 +1088,32 @@
Request:
<call ...>
<eacoin method="checkout">
- placeholder
+ <sessid __type="str" />
</eacoin>
</call>
Response:
<response>
- <eacoin status="status">
- placeholder
- </eacoin>
+ <eacoin status="status" />
</response>
eacoin.consume
Request:
<call ...>
- <eacoin method="consume">
- placeholder
+ <eacoin method="consume" esid="">
+ <sessid __type="str" />
+ <sequence __type="s16" />
+ <payment __type="s32" />
+ <service __type="s16" />
+ <itemtype __type="str" />
+ <detail __type="str" />
</eacoin>
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <acstatus __type="u8" />
+ <autocharge __type="u8" />
+ <balance __type="s32" />
</eacoin>
</response>
@@ -912,27 +1121,27 @@
Request:
<call ...>
<eacoin method="getbalance">
- placeholder
+ <sessid __type="str" />
</eacoin>
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <acstatus __type="u8" />
+ <balance __type="s32" />
</eacoin>
</response>
eacoin.getecstatus
Request:
<call ...>
- <eacoin method="getecstatus">
- placeholder
- </eacoin>
+ <eacoin method="getecstatus" />
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <ectype __type="str" />
+ <ecstatus __type="u8" />
</eacoin>
</response>
@@ -940,41 +1149,38 @@
Request:
<call ...>
<eacoin method="touch">
- placeholder
+ <sessid __type="str" />
</eacoin>
</call>
Response:
<response>
- <eacoin status="status">
- placeholder
- </eacoin>
+ <eacoin status="status" />
</response>
eacoin.opchpass
Request:
<call ...>
<eacoin method="opchpass">
- placeholder
+ <passwd __type="str" />
+ <newpasswd __type="str" />
</eacoin>
</call>
Response:
<response>
- <eacoin status="status">
- placeholder
- </eacoin>
+ <eacoin status="status" />
</response>
- eacoin.opchecking
+ eacoin.opcheckin
Request:
<call ...>
- <eacoin method="opchecking">
- placeholder
+ <eacoin method="opcheckin">
+ <passwd __type="str" />
</eacoin>
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <sessid __type="str" />
</eacoin>
</response>
@@ -982,27 +1188,54 @@
Request:
<call ...>
<eacoin method="opcheckout">
- placeholder
+ <sessid __type="str" />
</eacoin>
</call>
Response:
<response>
- <eacoin status="status">
- placeholder
- </eacoin>
+ <eacoin status="status" />
</response>
eacoin.getlog
Request:
<call ...>
<eacoin method="getlog">
- placeholder
+ <sessid __type="str" />
+ <logtype __type="str" />
+ <ectype __type="str" />
+ <target __type="str" />
+ <perpage __type="s16" />
+ <page __type="s16" />
+ <sesstype __type="str" />
</eacoin>
</call>
Response:
<response>
<eacoin status="status">
- placeholder
+ <processing __type="u8" />
+ <topic>
+ <sumdate __type="str" />
+ <sumfrom __type="str" />
+ <sumto __type="str" />
+
+ <today __type="s32" />
+ <average __type="s32" />
+ <total __type="s32" />
+ </topic>
+ <summary>
+ <items __type="s32" />
+ </summary>
+ <history>
+ <item[]>
+ <date __type="str" />
+ <consume __type="s32" />
+ <service __type="s32" />
+ <cardtype __type="str" />
+ <cardno __type="str" />
+ <title __type="str" />
+ <systemid __type="str" />
+ </item>
+ </history>
</eacoin>
</response>
@@ -1011,10 +1244,15 @@
traceroute.send
Request:
<call ...>
- <traceroute method="send">
- placeholder
+ <traceroute proto="" method="send">
+ <hop[]>
+ <valid __type="bool">
+ <addr __type="ip4">
+ <usec __type="u64">
+ </hop>
</traceroute>
</call>
+ hop
repeats for every hop (unsurprisingly)
Response:
<response>
<traceroute status="status">
diff --git a/styles.css b/styles.css
index b7fbc90..13908aa 100644
--- a/styles.css
+++ b/styles.css
@@ -1,6 +1,6 @@
body {
- /* font-family: sans-serif; */
- line-height: 1.25;
+ font-family: sans-serif;
+ line-height: 1.35;
max-width: 1000px;
margin: 16px auto;
color: #222;
@@ -9,12 +9,14 @@ body {
table {
border-collapse: collapse;
- font-family: monospace;
letter-spacing: .02em;
max-width: 100%;
overflow-x: auto;
display: block;
}
+table.code {
+ font-family: monospace;
+}
thead {
font-weight: bold;
@@ -24,9 +26,14 @@ thead {
td {
border: 1px solid #111;
padding: 2px;
- text-align: center;
min-width: 32px;
}
+table:not(.code) td {
+ padding: 2px 6px;
+}
+table.code td {
+ text-align: center;
+}
td a {
display: block;