This commit is contained in:
Bottersnike 2021-12-28 20:54:12 +00:00
parent 20affcfa63
commit 2da6e2b056
48 changed files with 2508 additions and 2875 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pyc
__pycache__/
build/

View File

@ -21,4 +21,4 @@
</table> </table>
</body> {% endblock %}

79
docs.py Normal file
View File

@ -0,0 +1,79 @@
from flask import Flask, send_from_directory, render_template
from livereload import Server
import os
app = Flask(__name__)
@app.route("/styles.css")
def styles():
return send_from_directory(".", "styles.css")
for base, folders, files in os.walk("images"):
for name in files:
def handler(base, name):
def handler():
return send_from_directory(base, name)
return handler
local_base = base.replace("\\", "/").strip(".").strip("/")
route = local_base + "/" + name
if not route.startswith("/"):
route = "/" + route
handler = handler(base, name)
handler.__name__ == route
app.add_url_rule(route, route, handler)
TEMPLATES = "templates"
PAGES_BASE = "pages"
for base, folders, files in os.walk(TEMPLATES + "/" + PAGES_BASE):
if ".git" in base:
continue
if base.startswith(TEMPLATES):
base = base[len(TEMPLATES):]
for name in files:
if name.endswith(".html"):
def handler(base, name):
def handler():
return render_template(os.path.join(base, name).strip("/").replace("\\", "/"))
return handler
local_base = base.replace("\\", "/").strip(".").strip("/")
if local_base.startswith(PAGES_BASE):
local_base = local_base[len(PAGES_BASE):]
route = local_base + "/" + name
if route.endswith("/index.html"):
route = route[:-10]
if not route.startswith("/"):
route = "/" + route
handler = handler(base, name)
handler.__name__ == route
app.add_url_rule(route, route, handler)
from flask import url_for
def has_no_empty_params(rule):
defaults = rule.defaults if rule.defaults is not None else ()
arguments = rule.arguments if rule.arguments is not None else ()
return len(defaults) >= len(arguments)
@app.route("/site-map")
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint))
return str(links)
if __name__ == '__main__':
app.config['TEMPLATES_AUTO_RELOAD'] = True
app.config['DEBUG'] = True
server = Server(app.wsgi_app)
server.watch("templates")
server.serve(port=3000)

7
freeze.py Normal file
View File

@ -0,0 +1,7 @@
from flask_frozen import Freezer
from docs import app
freezer = Freezer(app)
if __name__ == '__main__':
freezer.freeze()

View File

@ -1,106 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<tr>
<td><a href=".">Contents</a></td>
<td><a href="./transport.html">Transport layer</a></td>
<td><a href="./packet.html">Packet format</a></td>
<td><a href="./protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1>Benami/Konami e-Amusement API</h1>
<p>Why?</p>
<p>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).</p>
<p>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 <code>httac</code> and <code>xrpc</code>. 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.</p>
<p>These pages are very much a work in progress, and are being written <i>as</i> 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 (teach a man to fish, and all that).</p>
<p>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.</p>
<p>If you're here because you work on one of those aforementioned closed source projects, hello! Feel free to share
knowledge with the rest of the world, or point out corrections. Or don't; you do you.</p>
<h3>Code snippets</h3>
<p>Across these pages there are a number of code snippets. They roughly break down into three categories:</p>
<ul>
<li>Assembly: Directly disassembled code from game binaries</li>
<li>C: Either raw decompilation, or slightly cleaned up decompilation</li>
<li>Python: Snippets from my local testing implementations</li>
<li>Pseudocode: Used to illustrate some points. Note that it probably started life as Python before being
pseudo'd</li>
</ul>
<p>If you yoink chunks of Python code, attribution is always appreciated, but consider it under CC0 (just don't be
that person who tries to take credit for it, yeah?).</p>
<h2>Contents</h2>
<ol>
<li><a href="./transport.html">Transport layer</a></li>
<ol>
<li><a href="./transport.html#packet">Packet structure</a></li>
<li><a href="./transport.html#type">Types</a></li>
</ol>
<li><a href="./packet.html">The inner packet structure</a></li>
<ol>
<li><a href="./packet.html#xml">XML packets</a></li>
<li><a href="./packet.html#binary">Binary packed packets</a></li>
<li><a href="./packet.html#schema">Binary schemas</a></li>
<li><a href="./packet.html#data">Binary data</a></li>
</ol>
<li><a href="./protocol.html">Communication protocol details</a></li>
<ul>
<li>There are a crazy number of sub pages here, so just go check the contents there.</li>
</ul>
<li>Misc pages</li>
<ol>
<li><a href="./cardid.html">Parsing and converting card IDs</a></li>
</ol>
</ol>
<h2>Getting started</h2>
<p>My aim with these pages is to cover as much as possible, so you don't need to try and figure them out yourself.
That said, being able to follow along yourself will almost certainly help get more out of this. For following
along with source code, you're really going to want to grab yourself a dumped copy of a game (it's going to be a
lot easier, and cheeper, than dumping one yourself). I trust you can figure out where to find that.</p>
<p>For network related things, your options are a little broader. The ideal would be physical ownership of a
cabinet, and a subscription to genuine e-amusement. Odds are you don't have both of those :P. A connection to an
alternative network works just as well. In the more likely case that you don't have a physical cabinet, it's
time to crack out that dumped copy of a game and just run it on your own PC (or a VM, if you're not on Windows)
(odds are whatever you downloaded came with the program you'll need to start it pre-packaged. If not, it rhymes
with rice.).</p>
<p>You will also need a local e-amusement-emulating server. By the time I'm done with these pages, there will
hopefully be everything you need to be able to write your own. Unfortunately I'm not finished writing them;
depending on where you acquired your game, it may have shipped with one of said servers. If it didn't, Asphyxia
CORE will do the trick (yes, it's closed source).</p>
<p>If this all sounds like way too much work, and/or you're just here because of curiosity, I plan to prepare some
pcaps of network traffic to play around with without needing a running copy of a game or a network tap on a cab.
</p>
<a href="./transport.html">Next page</a>
<p><small>This site intentionally looks not-great. I don't feel like changing that, and honestly quite like the
aesthetic.</small></p>
</body>
</html>

View File

@ -1,896 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Packet format | eAmuse API</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<tr>
<td><a href=".">Contents</a></td>
<td><a href="./transport.html">Transport layer</a></td>
<td><a href="./packet.html">Packet format</a></td>
<td><a href="./protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1>Packet format</h1>
<p>eAmuse uses XML for its application layer payloads*. This XML is either verbatim, or in a custom packed binary
format.<br /><small>*Newer games use JSON, but this page is about XML.</small></p>
<h2 id="xml">The XML format</h2>
<p>Each tag that contains a value has a <code>__type</code> attribute that identifies what type it is. Array types
have a <code>__count</code> attribute indicating how many items are in the array. Binary blobs additionally have
a <code>__size</code> attribute indicating their length (this is notably not present on strings, however).</p>
<p>It is perhaps simpler to illustrate with an example, so:</p>
<pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;call model="KFC:J:A:A:2019020600" srcid="1000" tag="b0312077"&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str"&gt;G_CARDED&lt;/eventid&gt;
&lt;eventorder __type="s32"&gt;5&lt;/eventorder&gt;
&lt;pcbtime __type="u64"&gt;1639669516779&lt;/pcbtime&gt;
&lt;gamesession __type="s64"&gt;1&lt;/gamesession&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64"&gt;1&lt;/numdata1&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str"&gt;ea&lt;/locationid&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Arrays are encoded by concatenating every value together, with spaces between them. Data types that have multiple
values, are serialized similarly.</p>
<p>Therefore, an element storing an array of <code>3u8</code> (<code>[(1, 2, 3), (4, 5, 6)]</code>) would look like
this</p>
<pre><code>&lt;demo __type="3u8" __count="2"&gt;1 2 3 4 5 6&lt;/demo&gt;</code></pre>
<p>Besides this, this is otherwise a rather standard XML.</p>
<h2 id="binary">Packed binary overview</h2>
<p>Many packets, rather than using a string-based XML format, use a custom binary packed format instead. While it
can be a little confusing, remembering that this is encoding an XML tree can make it easier to parse.</p>
<p>To start with, let's take a look at the overall structure of the packets.</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td><i>A0</i></td>
<td>C</td>
<td>E</td>
<td>~E</td>
<td colspan="4">Head length</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Schema definition</td>
</tr>
<tr>
<td style="border-top: none;" colspan="12"></td>
<td colspan="1"><i>FF</i></td>
<td colspan="3">Align</td>
</tr>
<tr>
<td colspan="4">Data length</td>
<td style="border-bottom: none" colspan="12"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Payload</td>
</tr>
<tr>
<td style="border-top: none;" colspan="13"></td>
<td colspan="3">Align</td>
</tr>
</table>
<p>Every packet starts with the magic byte <code>0xA0</code>. Following this is the content byte, the encoding byte,
and then the 2's compliment of the encoding byte.</p>
<p>Currently known possible values for the content byte are:</p>
<table>
<thead>
<tr>
<td>C</td>
<td>Content</td>
</tr>
</thead>
<tr>
<td><code>0x42</code></td>
<td>Compressed data</td>
</tr>
<tr>
<td><code>0x43</code></td>
<td>Compressed, no data</td>
</tr>
<tr>
<td><code>0x45</code></td>
<td>Decompressed data</td>
</tr>
<tr>
<td><code>0x46</code></td>
<td>Decompressed, no data</td>
</tr>
</table>
<p>Decompressed packets contain an XML string. Compressed packets are what we're interested in here.</p>
<p>The encoding flag indicates the encoding for all string types in the packet (more on those later). Possible
values are:</p>
<table>
<thead>
<tr>
<td><code>E</code></td>
<td><code>~E</code></td>
<td colspan="3">Encoding name</td>
</tr>
</thead>
<tr>
<td><code>0x20</code></td>
<td><code>0xDF</code></td>
<td><code>ASCII</code></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>0x40</code></td>
<td><code>0xBF</code></td>
<td><code>ISO-8859-1</code></td>
<td><code>ISO_8859-1</code></td>
<td></td>
</tr>
<tr>
<td><code>0x60</code></td>
<td><code>0x9F</code></td>
<td><code>EUC-JP</code></td>
<td><code>EUCJP</code></td>
<td><code>EUC_JP</code></td>
</tr>
<tr>
<td><code>0x80</code></td>
<td><code>0x7F</code></td>
<td><code>SHIFT-JIS</code></td>
<td><code>SHIFT_JIS</code></td>
<td><code>SJIS</code></td>
</tr>
<tr>
<td><code>0xA0</code></td>
<td><code>0x5F</code></td>
<td><code>UTF-8</code></td>
<td><code>UTF8</code></td>
<td></td>
</tr>
</table>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs.</p>
<figure>
<img src="./images/encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b960</code></figcaption>
</figure>
<p>A second table exists just before this on in the source, responsible for the
<code>&lt;?xml version='1.0' encoding='??'?&gt;</code> line in XML files.
</p>
<figure>
<img src="./images/xml_encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b940</code></figcaption>
</figure>
<p>This is indexed using the following function, which maps the above encoding IDs to 1, 2, 3, 4 and 5
respectively.</p>
<pre><code>char* xml_get_encoding_name(uint encoding_id) {
return ENCODING_NAME_TABLE[((encoding_id & 0xe0) >> 5) * 4];
}</code></pre>
</details>
<p>While validating <code>~E</code> isn't technically required, it acts as a useful assertion that the packet being
parsed is valid.</p>
<h2 id="schema">The packet schema header</h2>
<p>Following the 4 byte header, is a 4 byte integer containing the length of the next part of the header (this is
technically made redundant as this structure is also terminated).</p>
<p>This part of the header defines the schema that the main payload uses.</p>
<p>A tag definition looks like:</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td>Type</td>
<td>nlen</td>
<td colspan="7">Tag name</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none;" colspan="15">Attributes and children</td>
<td colspan="1"><i>FE</i></td>
</tr>
</table>
<p>Structure names are encoded as densely packed 6 bit values, length prefixed (<code>nlen</code>). The acceptable
alphabet is <code>0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz</code>, and the packed values
are indecies within this alphabet.</p>
<p>The children can be a combination of either attribute names, or child tags. Attribute names are represented by
the byte <code>0x2E</code> followed by a length prefixed name as defined above. Child tags follow the above
format. Type <code>0x2E</code> must therefore be considered reserved as a possible structure type.</p>
<p>Attributes (type <code>0x2E</code>) represent a string attribute. Any other attribute must be defined as a child
tag. Is it notable that 0 children is allowable, which is how the majority of values are encoded.</p>
<p>All valid IDs, and their respective type, are listed in the following table. The bucket column here will be
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.</p>
<table class="code">
<thead>
<tr>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
<td></td>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
</tr>
</thead>
<tr>
<td>0x01</td>
<td>0</td>
<td>void</td>
<td>-</td>
<td>void</td>
<td></td>
<td></td>
<td>0x21</td>
<td>24</td>
<td>uint64[3]</td>
<td>int</td>
<td>3u64</td>
<td></td>
</tr>
<tr>
<td>0x02</td>
<td>1</td>
<td>int8</td>
<td>byte</td>
<td>s8</td>
<td></td>
<td></td>
<td>0x22</td>
<td>12</td>
<td>float[3]</td>
<td>int</td>
<td>3f</td>
<td></td>
</tr>
<tr>
<td>0x03</td>
<td>1</td>
<td>uint8</td>
<td>byte</td>
<td>u8</td>
<td></td>
<td></td>
<td>0x23</td>
<td>24</td>
<td>double[3]</td>
<td>int</td>
<td>3d</td>
<td></td>
</tr>
<tr>
<td>0x04</td>
<td>2</td>
<td>int16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x24</td>
<td>4</td>
<td>int8[4]</td>
<td>int</td>
<td>4s8</td>
<td></td>
</tr>
<tr>
<td>0x05</td>
<td>2</td>
<td>uint16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x25</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>4u8</td>
<td></td>
</tr>
<tr>
<td>0x06</td>
<td>4</td>
<td>int32</td>
<td>int</td>
<td>s32</td>
<td></td>
<td></td>
<td>0x26</td>
<td>8</td>
<td>int16[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x07</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>u32</td>
<td></td>
<td></td>
<td>0x27</td>
<td>8</td>
<td>uint8[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x08</td>
<td>8</td>
<td>int64</td>
<td>int</td>
<td>s64</td>
<td></td>
<td></td>
<td>0x28</td>
<td>16</td>
<td>int32[4]</td>
<td>int</td>
<td>4s32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x09</td>
<td>8</td>
<td>uint64</td>
<td>int</td>
<td>u64</td>
<td></td>
<td></td>
<td>0x29</td>
<td>16</td>
<td>uint32[4]</td>
<td>int</td>
<td>4u32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x0a</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>bin</td>
<td>binary</td>
<td></td>
<td>0x2a</td>
<td>32</td>
<td>int64[4]</td>
<td>int</td>
<td>4s64</td>
<td></td>
</tr>
<tr>
<td>0x0b</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>str</td>
<td>string</td>
<td></td>
<td>0x2b</td>
<td>32</td>
<td>uint64[4]</td>
<td>int</td>
<td>4u64</td>
<td></td>
</tr>
<tr>
<td>0x0c</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>ip4</td>
<td></td>
<td></td>
<td>0x2c</td>
<td>16</td>
<td>float[4]</td>
<td>int</td>
<td>4f</td>
<td>vf</td>
</tr>
<tr>
<td>0x0d</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>time</td>
<td></td>
<td></td>
<td>0x2d</td>
<td>32</td>
<td>double[4]</td>
<td>int</td>
<td>4d</td>
<td></td>
</tr>
<tr>
<td>0x0e</td>
<td>4</td>
<td>float</td>
<td>int</td>
<td>float</td>
<td>f</td>
<td></td>
<td>0x2e</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>attr</td>
<td></td>
</tr>
<tr>
<td>0x0f</td>
<td>8</td>
<td>double</td>
<td>int</td>
<td>double</td>
<td>d</td>
<td></td>
<td>0x2f</td>
<td>0</td>
<td></td>
<td>-</td>
<td>array</td>
<td></td>
</tr>
<tr>
<td>0x10</td>
<td>2</td>
<td>int8[2]</td>
<td>short</td>
<td>2s8</td>
<td></td>
<td></td>
<td>0x30</td>
<td>16</td>
<td>int8[16]</td>
<td>int</td>
<td>vs8</td>
<td></td>
</tr>
<tr>
<td>0x11</td>
<td>2</td>
<td>uint8[2]</td>
<td>short</td>
<td>2u8</td>
<td></td>
<td></td>
<td>0x31</td>
<td>16</td>
<td>uint8[16]</td>
<td>int</td>
<td>vu8</td>
<td></td>
</tr>
<tr>
<td>0x12</td>
<td>4</td>
<td>int16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x32</td>
<td>16</td>
<td>int8[8]</td>
<td>int</td>
<td>vs16</td>
<td></td>
</tr>
<tr>
<td>0x13</td>
<td>4</td>
<td>uint16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x33</td>
<td>16</td>
<td>uint8[8]</td>
<td>int</td>
<td>vu16</td>
<td></td>
</tr>
<tr>
<td>0x14</td>
<td>8</td>
<td>int32[2]</td>
<td>int</td>
<td>2s32</td>
<td></td>
<td></td>
<td>0x34</td>
<td>1</td>
<td>bool</td>
<td>byte</td>
<td>bool</td>
<td>b</td>
</tr>
<tr>
<td>0x15</td>
<td>8</td>
<td>uint32[2]</td>
<td>int</td>
<td>2u32</td>
<td></td>
<td></td>
<td>0x35</td>
<td>2</td>
<td>bool[2]</td>
<td>short</td>
<td>2b</td>
<td></td>
</tr>
<tr>
<td>0x16</td>
<td>16</td>
<td>int16[2]</td>
<td>int</td>
<td>2s64</td>
<td>vs64</td>
<td></td>
<td>0x36</td>
<td>3</td>
<td>bool[3]</td>
<td>int</td>
<td>3b</td>
<td></td>
</tr>
<tr>
<td>0x17</td>
<td>16</td>
<td>uint16[2]</td>
<td>int</td>
<td>2u64</td>
<td>vu64</td>
<td></td>
<td>0x37</td>
<td>4</td>
<td>bool[4]</td>
<td>int</td>
<td>4b</td>
<td></td>
</tr>
<tr>
<td>0x18</td>
<td>8</td>
<td>float[2]</td>
<td>int</td>
<td>2f</td>
<td></td>
<td></td>
<td>0x38</td>
<td>16</td>
<td>bool[16]</td>
<td>int</td>
<td>vb</td>
<td></td>
</tr>
<tr>
<td>0x19</td>
<td>16</td>
<td>double[2]</td>
<td>int</td>
<td>2d</td>
<td>vd</td>
<td></td>
<td>0x38</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1a</td>
<td>3</td>
<td>int8[3]</td>
<td>int</td>
<td>3s8</td>
<td></td>
<td></td>
<td>0x39</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1b</td>
<td>3</td>
<td>uint8[3]</td>
<td>int</td>
<td>3u8</td>
<td></td>
<td></td>
<td>0x3a</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1c</td>
<td>6</td>
<td>int16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3b</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1d</td>
<td>6</td>
<td>uint16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3c</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1e</td>
<td>12</td>
<td>int32[3]</td>
<td>int</td>
<td>3s32</td>
<td></td>
<td></td>
<td>0x3d</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1f</td>
<td>12</td>
<td>uint32[3]</td>
<td>int</td>
<td>3u32</td>
<td></td>
<td></td>
<td>0x3e</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x20</td>
<td>24</td>
<td>int64[3]</td>
<td>int</td>
<td>3s64</td>
<td></td>
<td></td>
<td>0x3f</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<p>Strings should be encoded and decoded according to the encoding specified in the packet header. Null termination is optional, however should be stripped during decoding.</p>
<p>All of these IDs are <code>& 0x3F</code>. Any value can be turned into an array by setting the 7<sup>th</sup> bit
high (<code>| 0x40</code>). Arrays of this form, in the data section, will be an aligned <code>size: u32</code>
immediately followed by <code>size</code> bytes' worth of (unaligned!) values of the unmasked type.</p>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs. This table contains the names of every tag, along
with additional information such as how many bytes that data type requires, and which parsing function
should be used.</p>
<figure>
<img src="./images/types_table.png">
<figcaption><code>libavs-win32.dll:0x100782a8</code></figcaption>
</figure>
</details>
<details>
<summary>Note about the <code>array</code> type:</summary>
<p>While I'm not totally sure, I have a suspicion this type is used internally as a pseudo-type. Trying to
identify its function as a parsable type has some obvious blockers:</p>
<p>All of the types have convenient <code>printf</code>-using helper functions that are used to emit them when
serializing XML. All except one.</p>
<img src="./images/no_array.png">
<p>If we have a look inside the function that populates node sizes (<code>libavs-win32.dll:0x1000cf00</code>),
it has an explicit case, however is the same fallback as the default case.</p>
<img src="./images/no_array_2.png">
<p>In the same function, however, we can find a second (technically first) check for the array type.</p>
<img src="./images/yes_array.png">
<p>This seems to suggest that internally arrays are represented as a normal node, with the <code>array</code>
type, however when serializing it's converted into the array types we're used to (well, will be after the
next sections) by masking 0x40 onto the contained type.</p>
<p>Also of interest from this snippet is the fact that <code>void</code>, <code>bin</code>, <code>str</code>,
and <code>attr</code> cannot be arrays. <code>void</code> and <code>attr</code> make sense, however
<code>str</code> and <code>bin</code> are more interesting. I suspect this is because konami want to be able
to preallocate the memory, which wouldn't be possible with these variable length structures.
</p>
</details>
<h2 id="data">The data section</h2>
<p>This is where all the actual packet data is. For the most part, parsing this is the easy part. We traverse our
schema, and read values out of the packet according to the value indicated in the schema. Unfortunately, konami
decided all data should be aligned very specifically, and that gaps left during alignment should be backfilled
later. This makes both reading and writing somewhat more complicated, however the system can be fairly easily
understood.</p>
<p>Firstly, we divide the payload up into 4 byte chunks. Each chunk can be allocated to either store individual
bytes, shorts, or ints (these are the buckets in the table above). When reading or writing a value, we first
check if a chunk allocated to the desired type's bucket is available and has free/as-yet-unread space within it.
If so, we will store/read our data to/from there. If there is no such chunk, we claim the next unclaimed chunk
for our bucket.</p>
<p>For example, imagine we write the sequence <code>byte, int, byte, short, byte, int, short</code>. The final output should look like:</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td>byte</td>
<td>byte</td>
<td>byte</td>
<td></td>
<td colspan="4">int</td>
<td colspan="2">short</td>
<td colspan="2">short</td>
<td colspan="4">int</td>
</tr>
</table>
<p>While this might seem a silly system compared to just not aligning values, it is at least possible to intuit that it helps reduce wasted space. It should be noted that any variable-length structure, such as a string or an array, claims all chunks it encroaches on for the <code>int</code> bucket, disallowing the storage of bytes or shorts within them.</p>
<details>
<summary>Implementing a packer</summary>
<p>While the intuitive way to understand the packing algorithm is via chunks and buckets, a far more efficient implementation can be made that uses three pointers. Rather than try to explain in words, hopefully this python implementation should suffice as explanation:<pre><code>class Packer:
def __init__(self, offset=0):
self._word_cursor = offset
self._short_cursor = offset
self._byte_cursor = offset
self._boundary = offset % 4
def _next_block(self):
self._word_cursor += 4
return self._word_cursor - 4
def request_allocation(self, size):
if size == 0:
return self._word_cursor
elif size == 1:
if self._byte_cursor % 4 == self._boundary:
self._byte_cursor = self._next_block() + 1
else:
self._byte_cursor += 1
return self._byte_cursor - 1
elif size == 2:
if self._short_cursor % 4 == self._boundary:
self._short_cursor = self._next_block() + 2
else:
self._short_cursor += 2
return self._short_cursor - 2
else:
old_cursor = self._word_cursor
for _ in range(math.ceil(size / 4)):
self._word_cursor += 4
return old_cursor
def notify_skipped(self, no_bytes):
for _ in range(math.ceil(no_bytes / 4)):
self.request_allocation(4)</code></pre></p>
</details>
<a href="./transport.html">Prev page</a> | <a href="./protocol.html">Next page</a>
</body>
</html>

View File

@ -1,33 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>apsmanager</code></h1>
<h2 id="getstat"><code>apsmanager.getstat</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;apsmanager method="getstat" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;apsmanager status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,232 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>cardmng</code></h1>
<p>As the name might imply, this service is responsible for handling interactions with physical e-Amusement cards.
e-Amusement currently has two different types of cards in circulation. There are classic e-Amusement cards
making use of a magnetic stripe, and the newer RFID cards using FeliCa (these are probably what you have). They
are identified in requests using the <code>cardtype</code> attribute as in the below table.
</p>
<p>e-Amusement cards have a "card number" and a "card id". Confusingly, neither is a number. The card number is the
one printed on your card. The card ID is your KONAMI ID. You can (and should) read about the algorithm used for
these IDs on <a href="../cardid.html">the Card IDs page</a>.</p>
<p>In the interest of not wasting space, <code>cardid</code> and <code>cardtype</code> will be omitted from
individual breakdowns where their meaning is obvious.</p>
<h4>Card types:</h4>
<table>
<thead>
<tr>
<td><code>cardtype</code></td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>1</code></td>
<td>Old style magnetic stripe card</td>
</tr>
<tr>
<td><code>2</code></td>
<td>FeliCa RFID card</td>
</tr>
</table>
<ul>
<li><code><a href="#inquire">cardmng.inquire</a></code></li>
<li><code><a href="#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="#authpass">cardmng.authpass</a></code></li>
<li><code><a href="#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<h2 id="inquire"><code>cardmng.inquire</code></h2>
<p>Request information about a card that has been inserted or touched against a reader.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="inquire" cardid="" cardtype="" update="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>update</code></td>
<td>Should the tracked last play time be updated by this inquiry? (Just a guess)</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" newflag="" binded="" expired=" ecflag="" useridflag="" extidflag="" lastupdate="" /&gt;
&lt;/response&gt;</code></pre>
<p>To handle this request, we first must lookup if this <code>cardid</code> has ever been seen by our servers
before. If not, we abort with a <code>112</code> status. Otherwise, we proceeed to check if this card has been
seen for this specific game. If we have never seen this card used on this game, it is possible this card was
used with an older version of this game, and migration is supported, in which case we report as if we had found
a profile for this game.</p>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>newflag</code></td>
<td>Inverse of <code>binded</code></td>
</tr>
<tr>
<td><code>binded</code></td>
<td>Has a profile ever been created for this game (or an older version, requiring a migration)
(<code>1</code> or <code>0</code>)</td>
</tr>
<tr>
<td><code>expired</code></td>
<td>? Just set to <code>0</code>.</td>
</tr>
</table>
<h2 id="getrefid"><code>cardmng.getrefid</code></h2>
<p>Register a new card to this server.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>newflag</code></td>
<td>?</td>
</tr>
<tr>
<td><code>passwd</code></td>
<td>The pin for this new user. <i>Should</i> always be a four digit number (and that's worth validating),
but it's passed as a string so could feasibly be anything desired.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" /&gt;
&lt;/response&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>pcode</code></td>
<td>? Not present in captured data.</td>
</tr>
</table>
<h2 id="bindmodel"><code>cardmng.bindmodel</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindmodel" refid="" newflag="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" dataid="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="bindcard"><code>cardmng.bindcard</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindcard" cardtype="" newid="" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="authpass"><code>cardmng.authpass</code></h2>
<p>Test a pin for a card. This request notably uses the <code>refid</code>, so required a
<code>cardmng.inquire</code> call to be made first.
</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="authpass" refid="" pass="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>The reference we received either during <code>cardmng.inquire</code> or <code>cardmng.getrefid</code>
(the latter for new cards)</td>
</tr>
<tr>
<td><code>pass</code></td>
<td>The pin to test. See <code>cardmng.getrefid</code>.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<p>If the pin is valid, status should be <code>0</code>. Otherwise, <code>116</code>.</p>
<h2 id="getkeepspan"><code>cardmng.getkeepspan</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepspan" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepspan="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getkeepremain"><code>cardmng.getkeepremain</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepremain" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepremain="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getdatalist"><code>cardmng.getdatalist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getdatalist" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>"&gt;
&lt;item[]&gt;
&lt;mcode __type="str" /&gt;
&lt;dataid __type="str" /&gt;
&lt;regtime __type="str" /&gt;
&lt;lasttime __type="str" /&gt;
&lt;exptime __type="str" /&gt;
&lt;expflag __type="u8" /&gt;
&lt;/item[]&gt;
&lt;/cardmng&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>dlstatus</code></h1>
<h2 id="done"><code>dlstatus.done</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="done"&gt;
&lt;url&gt;
&lt;param __type="str" /&gt;
&lt;/url&gt;
&lt;name __type="str" /&gt;
&lt;size __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>"&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/response&gt;</code></pre>
<h2 id="progress"><code>dlstatus.progress</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="progress" /&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,37 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>esign</code></h1>
<h2 id="request"><code>esign.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esign method="request"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esign status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,50 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>esoc</code></h1>
<h2 id="read"><code>esoc.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="read"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>"&gt;
&lt;recvdata /&gt;
&lt;/esoc&gt;
&lt;/response&gt;</code></pre>
<p>Go figure.</p>
<h2 id="write"><code>esoc.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="write"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,58 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>eventlog</code></h1>
<h2 id="write"><code>eventlog.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str" /&gt;
&lt;eventorder __type="s32" /&gt;
&lt;pcbtime __type="u64" /&gt;
&lt;gamesession __type="s64" /&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64" /&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str" /&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Event ID list:</p>
<ul>
<li><code>G_GAMED</code></li>
<li><code>S_ERROR</code></li>
<li><code>S_PWRON</code> <b>TODO: find more!</b></li>
<li><code>T_OTDEMO</code></li>
</ul>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eventlog status="<i>status</i>"&gt;
&lt;gamesession __type="s64" /&gt;
&lt;logsendflg __type="s32" /&gt;
&lt;logerrlevel __type="s32" /&gt;
&lt;evtidnosendflg __type="s32" /&gt;
&lt;/eventlog&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,35 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>message</code></h1>
<h2 id="get"><code>message.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;message method="get" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;message expire="" status="<i>status</i>"&gt;
&lt;item[] name="" start="" end="" data="" /&gt;
&lt;/message&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,47 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>package</code></h1>
<h2 id="list"><code>package.list</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="list" pkgtype="<i>pkgtype</i>" model*="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>all</code> is the only currently observed value for <code>pkgtype</code></p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>"&gt;
&lt;item[] url="" /&gt;
&lt;/package&gt;
&lt;/response&gt;</code></pre>
<p>A list of all packages available for download.</p>
<h2 id="intend"><code>package.intend</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="intend" url="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,41 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>pcbevent</code></h1>
<h2 id="put"><code>pcbevent.put</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbevent method="put"&gt;
&lt;time __type="time" /&gt;
&lt;seq __type="u32" /&gt;
&lt;item[]&gt;
&lt;name __type="str" /&gt;
&lt;value __type="s32" /&gt;
&lt;time __type="time" /&gt;
&lt;/item[]&gt;
&lt;/pcbevent&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbevent status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,39 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>pcbtracker</code></h1>
<h2 id="alive"><code>pcbtracker.alive</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbtracker method="alive" model*="" hardid="" softid="" accountid="" agree="" ecflag="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>ecflag</code> here is determining if the arcade operator allows the use of paseli on this machine.</p>
<p><code>agree@</code> and <code>ecflag@</code> appear to either be totally non present, or present with a value of
<code>"1"</code>, but then again I may be reading the code wrong, so take that with a pinch of salt.
</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbtracker status="" time="" limit="" ecenable="" eclimit="" &gt;
&lt;/response&gt;</code></pre>
<p>As you might guess, <code>ecenable@</code> is therefore the flag to determine if paseli is enabled (i.e. the
arcade operator and the server both allow its use).</p>
</body>

View File

@ -1,70 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>services</code></h1>
<h2 id="get"><code>services.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;services method="get" model*="" &gt;
&lt;info&gt;
&lt;AVS2 __type="str"&gt;<i>AVS2 version</i>&lt;/AVS2&gt;
&lt;/info&gt;
&lt;/services&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;services expire="" method="get" mode="" status="<i>status</i>"&gt;
&lt;item[] name="<i>service</i>" url="<i>url</i>" /&gt;
&lt;/services&gt;
&lt;/response&gt;</code></pre>
<p>Known services are:</p>
<ul>
<li><code>ntp</code></li>
<li><code>keepalive</code></li>
<li><code>cardmng</code></li>
<li><code>facility</code></li>
<li><code>message</code></li>
<li><code>numbering</code></li>
<li><code>package</code></li>
<li><code>pcbevent</code></li>
<li><code>pcbtracker</code></li>
<li><code>pkglist</code></li>
<li><code>posevent</code></li>
<li><code>userdata</code></li>
<li><code>userid</code></li>
<li><code>eacoin</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>dlstatus</code></li>
<li><code>netlog</code></li>
<li><code>sidmgr</code></li>
<li><code>globby</code></li>
</ul>
<p>Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). <code>ntp</code> is a
notable exception, unless you're planning on reimplementing NTP. <code>keepalive</code> will likely alsop be a
custom URL with query parameters pre-baked.</p>
<p><code>mode</code> is one of <code>operation</code>, <code>debug</code>, <code>test</code>, or
<code>factory</code>.
</p>
</body>

View File

@ -1,94 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>sidmgr</code></h1>
<h2 id="create"><code>sidmgr.create</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="create"&gt;
&lt;cardtype __type="str" /&gt;
&lt;cardid __type="str" /&gt;
&lt;cardgid __type="str" /&gt;
&lt;steal __type="u8" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;e_count __type="u8" /&gt;
&lt;last __type="time" /&gt;
&lt;locked __type="time" /&gt;
&lt;sid __type="str" /&gt;
&lt;cardid_status __type="u8" /&gt;
&lt;refid __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="open"><code>sidmgr.open</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="open" sid="" &gt;
&lt;pass __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;refid __type="str" /&gt;
&lt;locked __type="time" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="touch"><code>sidmgr.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="touch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="branch"><code>sidmgr.branch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="branch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="close"><code>sidmgr.close</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="close" sid="" /&gt;
&lt;cause __type="u32" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>traceroute</code></h1>
<h2 id="send"><code>traceroute.send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;traceroute proto="" method="send"&gt;
&lt;hop[]&gt;
&lt;valid __type="bool"&gt;
&lt;addr __type="ip4"&gt;
&lt;usec __type="u64"&gt;
&lt;/hop[]&gt;
&lt;/traceroute&gt;
&lt;/call&gt;</code></pre>
<p><code>hop</code> repeats for every hop (unsurprisingly)</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;traceroute status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,48 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>userdata</code></h1>
<h2 id="read"><code>userdata.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="read" card*="" model*="" label="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" time=""&gt;
&lt;b[] __type="" /&gt;
&lt;/userdata&gt;
&lt;/response&gt;</code></pre>
<p><code>__type</code> here can be either <code>bin</code> or <code>str</code></p>
<h2 id="write"><code>userdata.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="write" card="" time="" model*="" label*="" &gt;
&lt;b[] __type="str" /&gt;
&lt;/userdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
</body>

View File

@ -1,284 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Packet format | eAmuse API</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<tr>
<td><a href=".">Contents</a></td>
<td><a href="./transport.html">Transport layer</a></td>
<td><a href="./packet.html">Packet format</a></td>
<td><a href="./protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1>Application Protocol</h1>
<p>As the previous pages have eluded to (you <i>did</i> read them, didn't you?), eAmuse uses HTTP as its main way of
getting data around. This means we need an HTTP server running but, as we'll see, we don't need to think too
hard about that.</p>
<p>Every request made is a <code>POST</code> request, to <code>//&lt;model&gt;/&lt;module&gt;/&lt;method&gt;</code>,
with its body being encoded data as described in the previous sections. In addition to the
<code>X-Compress:</code> and <code>X-Eamuse-Info:</code> headers previously detailed, there is also a
<code>X-PCB-ID:</code> header. that can be set. Your machine's PCB ID uniquely defines the physical board. This
header is added in out-bound requests, and allows the server to identify you. Importantly, it's also the value
that the server uses to identify which machines are authorized to be on the network, and which are not.
</p>
<p>Every request is followed immediately by a response. Any response code other than <code>200</code> is considered
a failure.</p>
<details>
<summary>Source code details</summary>
<figure>
<img src="images/200_only.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000f8e7</code></figcaption>
</figure>
</details>
<p>All requests follow a basic format:</p>
<pre><code>&lt;call model="<i>model</i>" srcid="<i>srcid</i>" tag="<i>tag</i>"&gt;
&lt;<i>module</i> method="<i>method</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/call&gt;</code></pre>
<p>The responses follow a similar format:</p>
<pre><code>&lt;response&gt;
&lt;<i>module</i> status="<i>status</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/response&gt;</code></pre>
<p>With <code>"0"</code> being a successful status. Convention is to identify a specific method as
<code><i>module</i>.<i>method</i></code>, and we'll be following this convention in this document too. There are
a <i>lot</i> of possible methods, so the majority of this document is a big reference for them all. There are a
number of generic methods, and a number of game specific ones. If you haven't clocked yet, I've been working on
an SDVX 4 build for most of these pages, and each game also comes with its own set of game-specific methods.
These are namespaces under the <code>game.%s</code> module and, in the case of SDVX 4, are all
<code>game.sv4_<i>method</i></code>. I may or may not document the SDVX 4 specific methods, but I've listed them
here anyway for completeness.
</p>
<p>Paths in the XML bodies are formatted using an XPath-like syntax. That is, <code>status@/response</code> gets the
<code>status</code> attribute from <code>response</code>, and <code>response/eacoin/sequence</code> would return
that node's value.
</p>
<p><b>NOTE:</b> I am using the non-standard notation of <code>&lt;node* ...</code> and
<code>&lt;node attr*="" ...</code> to indicate that an attribute or node is not always present! Additionally, I
am going to use the notation of <code>&lt;node[]&gt;</code> to indicate that a node repeats.
</p>
<table>
<thead>
<tr>
<td>Status</td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>0</code></td>
<td>Success</td>
</tr>
<tr>
<td><code>109</code></td>
<td>No profile</td>
</tr>
<tr>
<td><code>110</code></td>
<td>Not allowed</td>
</tr>
<tr>
<td><code>112</code></td>
<td>Card not found (<code>cardmng.inquire</code>)</td>
</tr>
<tr>
<td><code>116</code></td>
<td>Card pin invalid (<code>cardmng.authpass</code>)</td>
</tr>
</table>
<details>
<summary>How to reverse engineer these calls</summary>
<p>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:
</p>
<details>
<summary>Ghidra</summary>
<img src="images/eventlog_ghidra.png">
<img src="images/matching_request_ghidra.png">
</details>
<details>
<summary>IDA Pro</summary>
<img src="images/eventlog_ida.png">
<img src="images/matching_request_ida.png">
</details>
<p>I know which of these I'd rather use for reverse engineering (sorry, Ghidra)!</p>
</details>
<h2>Possible XRPC requests</h2>
<ul>
<li><code><a href="proto/eventlog.html">eventlog.%s</a></code></li>
<ul>
<li><code><a href="proto/eventlog.html#eventlog.write">eventlog.write</a></code></li>
</ul>
<li><code><a href="proto/playerdata.html">playerdata.%s</a></code></li>
<ul>
<li><code><a href="proto/playerdata.html#usergamedata_send">playerdata.usergamedata_send</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_recv">playerdata.usergamedata_recv</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_inheritance">playerdata.usergamedata_inheritance</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_condrecv">playerdata.usergamedata_condrecv</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_scorerank">playerdata.usergamedata_scorerank</a></code>
</li>
</ul>
<li><code><a href="proto/matching.html">matching.%s</a></code></li>
<ul>
<li><code><a href="proto/matching.html#request">matching.request</a></code></li>
<li><code><a href="proto/matching.html#wait">matching.wait</a></code></li>
<li><code><a href="proto/matching.html#finish">matching.finish</a></code></li>
</ul>
<li><code><a href="proto/system.html">system.%s</a></code></li>
<ul>
<li><code><a href="proto/system.html#getmaster">system.getmaster</a></code></li>
<li><code><a href="proto/system.html#getlocationiplist">system.getlocationiplist</a></code></li>
<li><code><a href="proto/system.html#xrpcproxy">system.xrpcproxy</a></code></li>
<li><code><a href="proto/system.html#convcardnumber">system.convcardnumber</a></code></li>
</ul>
<li><code><a href="proto/esoc.html">esoc.%s</a></code></li>
<ul>
<li><code><a href="proto/esoc.html#read">esoc.read</a></code></li>
<li><code><a href="proto/esoc.html#write">esoc.write</a></code></li>
</ul>
<li><code><a href="proto/cardmng.html">cardmng.%s</a></code></li>
<ul>
<li><code><a href="proto/cardmng.html#inquire">cardmng.inquire</a></code></li>
<li><code><a href="proto/cardmng.html#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="proto/cardmng.html#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="proto/cardmng.html#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="proto/cardmng.html#authpass">cardmng.authpass</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="proto/cardmng.html#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<li><code><a href="proto/esign.html">esign.%s</a></code></li>
<ul>
<li><code><a href="proto/esign.html#request">esign.request</a></code></li>
</ul>
<li><code><a href="proto/package.html">package.%s</a></code></li>
<ul>
<li><code><a href="proto/package.html#list">package.list</a></code></li>
<li><code><a href="proto/package.html#intend">package.intend</a></code></li>
</ul>
<li><code><a href="proto/userdata.html">userdata.%s</a></code></li>
<ul>
<li><code><a href="proto/userdata.html#read">userdata.read</a></code></li>
<li><code><a href="proto/userdata.html#write">userdata.write</a></code></li>
</ul>
<li><code><a href="proto/services.html">services.%s</a></code></li>
<ul>
<li><code><a href="proto/services.html#get">services.get</a></code></li>
</ul>
<li><code><a href="proto/pcbtracker.html">pcbtracker.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbtracker.html#alive">pcbtracker.alive</a></code></li>
</ul>
<li><code><a href="proto/pcbevent.html">pcbevent.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbevent.html#put">pcbevent.put</a></code></li>
</ul>
<li><code><a href="proto/message.html">message.%s</a></code></li>
<ul>
<li><code><a href="proto/message.html#get">message.get</a></code></li>
</ul>
<li><code><a href="proto/facility.html">facility.%s</a></code></li>
<ul>
<li><code><a href="proto/facility.html#get">facility.get</a></code></li>
</ul>
<li><code><a href="proto/apsmanager.html">apsmanager.%s</a></code></li>
<ul>
<li><code><a href="proto/apsmanager.html#getstat">apsmanager.getstat</a></code></li>
</ul>
<li><code><a href="proto/sidmgr.html">sidmgr.%s</a></code></li>
<ul>
<li><code><a href="proto/sidmgr.html#create">sidmgr.create</a></code></li>
<li><code><a href="proto/sidmgr.html#open">sidmgr.open</a></code></li>
<li><code><a href="proto/sidmgr.html#touch">sidmgr.touch</a></code></li>
<li><code><a href="proto/sidmgr.html#branch">sidmgr.branch</a></code></li>
<li><code><a href="proto/sidmgr.html#close">sidmgr.close</a></code></li>
</ul>
<li><code><a href="proto/dlstatus.html">dlstatus.%s</a></code></li>
<ul>
<li><code><a href="proto/dlstatus.html#done">dlstatus.done</a></code></li>
<li><code><a href="proto/dlstatus.html#progress">dlstatus.progress</a></code></li>
</ul>
<li><code><a href="proto/eacoin.html">eacoin.%s</a></code></li>
<ul>
<li><code><a href="proto/eacoin.html#checkin">eacoin.checkin</a></code></li>
<li><code><a href="proto/eacoin.html#checkout">eacoin.checkout</a></code></li>
<li><code><a href="proto/eacoin.html#consume">eacoin.consume</a></code></li>
<li><code><a href="proto/eacoin.html#getbalance">eacoin.getbalance</a></code></li>
<li><code><a href="proto/eacoin.html#getecstatus">eacoin.getecstatus</a></code></li>
<li><code><a href="proto/eacoin.html#touch">eacoin.touch</a></code></li>
<li><code><a href="proto/eacoin.html#opchpass">eacoin.opchpass</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckin">eacoin.opcheckin</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckout">eacoin.opcheckout</a></code></li>
<li><code><a href="proto/eacoin.html#getlog">eacoin.getlog</a></code></li>
</ul>
<li><code><a href="proto/traceroute.html">traceroute.%s</a></code></li>
<ul>
<li><code><a href="proto/traceroute.html#send">traceroute.send</a></code></li>
</ul>
<li><code><a href="proto/game/sv4.html">game.%s</a></code></li>
<ul>
<li><code><a href="proto/game/sv4.html#sample">game.sv4_sample</a></code></li>
<li><code><a href="proto/game/sv4.html#new">game.sv4_new</a></code></li>
<li><code><a href="proto/game/sv4.html#load">game.sv4_load</a></code></li>
<li><code><a href="proto/game/sv4.html#load_m">game.sv4_load_m</a></code></li>
<li><code><a href="proto/game/sv4.html#save">game.sv4_save</a></code></li>
<li><code><a href="proto/game/sv4.html#save_m">game.sv4_save_m</a></code></li>
<li><code><a href="proto/game/sv4.html#common">game.sv4_common</a></code></li>
<li><code><a href="proto/game/sv4.html#shop">game.sv4_shop</a></code></li>
<li><code><a href="proto/game/sv4.html#hiscore">game.sv4_hiscore</a></code></li>
<li><code><a href="proto/game/sv4.html#buy">game.sv4_buy</a></code></li>
<li><code><a href="proto/game/sv4.html#exception">game.sv4_exception</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_s">game.sv4_entry_s</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_e">game.sv4_entry_e</a></code></li>
<li><code><a href="proto/game/sv4.html#frozen">game.sv4_frozen</a></code></li>
<li><code><a href="proto/game/sv4.html#lounce">game.sv4_lounce</a></code></li>
<li><code><a href="proto/game/sv4.html#save_e">game.sv4_save_e</a></code></li>
<li><code><a href="proto/game/sv4.html#save_pb">game.sv4_save_pb</a></code></li>
<li><code><a href="proto/game/sv4.html#save_c">game.sv4_save_c</a></code></li>
<li><code><a href="proto/game/sv4.html#play_s">game.sv4_play_s</a></code></li>
<li><code><a href="proto/game/sv4.html#play_e">game.sv4_play_e</a></code></li>
<li><code><a href="proto/game/sv4.html#serial">game.sv4_serial</a></code></li>
<li><code><a href="proto/game/sv4.html#save_fi">game.sv4_save_fi</a></code></li>
<li><code><a href="proto/game/sv4.html#print">game.sv4_print</a></code></li>
<li><code><a href="proto/game/sv4.html#print_h">game.sv4_print_h</a></code></li>
<li><code><a href="proto/game/sv4.html#load_r">game.sv4_load_r</a></code></li>
<li><code><a href="proto/game/sv4.html#save_campaign">game.sv4_save_campaign</a></code></li>
</ul>
</ul>
<b>Totally undocumented services (based on <code>services.get</code>):</b>
<ul>
<li><code>numbering</code></li>
<li><code>pkglist</code></li>
<li><code>userid</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>netlog</code></li>
<li><code>globby</code></li>
</ul>
<p>I'll try and figure these out in due course, promise!</p>
</body>

25
templates/base.html Normal file
View File

@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}{% endblock %}{% if self.title() %} | {% endif %}e-Amusement API</title>
<link rel="stylesheet" href="{{ROOT}}/styles.css">
</head>
<body>
<table>
<tr>
<td><a href="{{ROOT}}/">Contents</a></td>
<td><a href="{{ROOT}}/transport.html">Transport layer</a></td>
<td><a href="{{ROOT}}/packet.html">Packet format</a></td>
<td><a href="{{ROOT}}/protocol.html">Application Protocol</a></td>
</tr>
</table>
{% block body %}{% endblock %}
</body>
</html>

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<tr>
<td><a href=".">Contents</a></td>
<td><a href="transport.html">Transport layer</a></td>
<td><a href="packet.html">Packet format</a></td>
<td><a href="protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1>Card ID generation</h1> <h1>Card ID generation</h1>
<details> <details>
<summary>I'm just here for code.</summary> <summary>I'm just here for code.</summary>
@ -484,5 +464,4 @@ def load_key(key):
des3_setkey(KEY_DATA, key_data)</code></pre> des3_setkey(KEY_DATA, key_data)</code></pre>
</details> </details>
</details> </details>
{% endblock %}
</body>

View File

@ -0,0 +1,84 @@
{% extends "base.html" %}
{% block body %}
<h1>Benami/Konami e-Amusement API</h1>
<p>Why?</p>
<p>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).</p>
<p>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 <code>httac</code> and <code>xrpc</code>. 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.</p>
<p>These pages are very much a work in progress, and are being written <i>as</i> 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 (teach a man to fish, and all that).</p>
<p>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.</p>
<p>If you're here because you work on one of those aforementioned closed source projects, hello! Feel free to share
knowledge with the rest of the world, or point out corrections. Or don't; you do you.</p>
<h3>Code snippets</h3>
<p>Across these pages there are a number of code snippets. They roughly break down into three categories:</p>
<ul>
<li>Assembly: Directly disassembled code from game binaries</li>
<li>C: Either raw decompilation, or slightly cleaned up decompilation</li>
<li>Python: Snippets from my local testing implementations</li>
<li>Pseudocode: Used to illustrate some points. Note that it probably started life as Python before being
pseudo'd</li>
</ul>
<p>If you yoink chunks of Python code, attribution is always appreciated, but consider it under CC0 (just don't be
that person who tries to take credit for it, yeah?).</p>
<h2>Contents</h2>
<ol>
<li><a href="./transport.html">Transport layer</a></li>
<ol>
<li><a href="./transport.html#packet">Packet structure</a></li>
<li><a href="./transport.html#type">Types</a></li>
</ol>
<li><a href="./packet.html">The inner packet structure</a></li>
<ol>
<li><a href="./packet.html#xml">XML packets</a></li>
<li><a href="./packet.html#binary">Binary packed packets</a></li>
<li><a href="./packet.html#schema">Binary schemas</a></li>
<li><a href="./packet.html#data">Binary data</a></li>
</ol>
<li><a href="./protocol.html">Communication protocol details</a></li>
<ul>
<li>There are a crazy number of sub pages here, so just go check the contents there.</li>
</ul>
<li>Misc pages</li>
<ol>
<li><a href="./cardid.html">Parsing and converting card IDs</a></li>
</ol>
</ol>
<h2>Getting started</h2>
<p>My aim with these pages is to cover as much as possible, so you don't need to try and figure them out yourself.
That said, being able to follow along yourself will almost certainly help get more out of this. For following
along with source code, you're really going to want to grab yourself a dumped copy of a game (it's going to be a
lot easier, and cheeper, than dumping one yourself). I trust you can figure out where to find that.</p>
<p>For network related things, your options are a little broader. The ideal would be physical ownership of a
cabinet, and a subscription to genuine e-amusement. Odds are you don't have both of those :P. A connection to an
alternative network works just as well. In the more likely case that you don't have a physical cabinet, it's
time to crack out that dumped copy of a game and just run it on your own PC (or a VM, if you're not on Windows)
(odds are whatever you downloaded came with the program you'll need to start it pre-packaged. If not, it rhymes
with rice.).</p>
<p>You will also need a local e-amusement-emulating server. By the time I'm done with these pages, there will
hopefully be everything you need to be able to write your own. Unfortunately I'm not finished writing them;
depending on where you acquired your game, it may have shipped with one of said servers. If it didn't, Asphyxia
CORE will do the trick (yes, it's closed source).</p>
<p>If this all sounds like way too much work, and/or you're just here because of curiosity, I plan to prepare some
pcaps of network traffic to play around with without needing a running copy of a game or a network tap on a cab.
</p>
<a href="./transport.html">Next page</a>
<p><small>This site intentionally looks not-great. I don't feel like changing that, and honestly quite like the
aesthetic.</small></p>
{% endblock %}

881
templates/pages/packet.html Normal file
View File

@ -0,0 +1,881 @@
{% extends "base.html" %}
{% block body %}
<h1>Packet format</h1>
<p>eAmuse uses XML for its application layer payloads*. This XML is either verbatim, or in a custom packed binary
format.<br /><small>*Newer games use JSON, but this page is about XML.</small></p>
<h2 id="xml">The XML format</h2>
<p>Each tag that contains a value has a <code>__type</code> attribute that identifies what type it is. Array types
have a <code>__count</code> attribute indicating how many items are in the array. Binary blobs additionally have
a <code>__size</code> attribute indicating their length (this is notably not present on strings, however).</p>
<p>It is perhaps simpler to illustrate with an example, so:</p>
<pre><code>&lt;?xml version='1.0' encoding='UTF-8'?&gt;
&lt;call model="KFC:J:A:A:2019020600" srcid="1000" tag="b0312077"&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str"&gt;G_CARDED&lt;/eventid&gt;
&lt;eventorder __type="s32"&gt;5&lt;/eventorder&gt;
&lt;pcbtime __type="u64"&gt;1639669516779&lt;/pcbtime&gt;
&lt;gamesession __type="s64"&gt;1&lt;/gamesession&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64"&gt;1&lt;/numdata1&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str"&gt;ea&lt;/locationid&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Arrays are encoded by concatenating every value together, with spaces between them. Data types that have multiple
values, are serialized similarly.</p>
<p>Therefore, an element storing an array of <code>3u8</code> (<code>[(1, 2, 3), (4, 5, 6)]</code>) would look like
this</p>
<pre><code>&lt;demo __type="3u8" __count="2"&gt;1 2 3 4 5 6&lt;/demo&gt;</code></pre>
<p>Besides this, this is otherwise a rather standard XML.</p>
<h2 id="binary">Packed binary overview</h2>
<p>Many packets, rather than using a string-based XML format, use a custom binary packed format instead. While it
can be a little confusing, remembering that this is encoding an XML tree can make it easier to parse.</p>
<p>To start with, let's take a look at the overall structure of the packets.</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td><i>A0</i></td>
<td>C</td>
<td>E</td>
<td>~E</td>
<td colspan="4">Head length</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Schema definition</td>
</tr>
<tr>
<td style="border-top: none;" colspan="12"></td>
<td colspan="1"><i>FF</i></td>
<td colspan="3">Align</td>
</tr>
<tr>
<td colspan="4">Data length</td>
<td style="border-bottom: none" colspan="12"></td>
</tr>
<tr>
<td style="border-top: none; border-bottom: none;" colspan="16">Payload</td>
</tr>
<tr>
<td style="border-top: none;" colspan="13"></td>
<td colspan="3">Align</td>
</tr>
</table>
<p>Every packet starts with the magic byte <code>0xA0</code>. Following this is the content byte, the encoding byte,
and then the 2's compliment of the encoding byte.</p>
<p>Currently known possible values for the content byte are:</p>
<table>
<thead>
<tr>
<td>C</td>
<td>Content</td>
</tr>
</thead>
<tr>
<td><code>0x42</code></td>
<td>Compressed data</td>
</tr>
<tr>
<td><code>0x43</code></td>
<td>Compressed, no data</td>
</tr>
<tr>
<td><code>0x45</code></td>
<td>Decompressed data</td>
</tr>
<tr>
<td><code>0x46</code></td>
<td>Decompressed, no data</td>
</tr>
</table>
<p>Decompressed packets contain an XML string. Compressed packets are what we're interested in here.</p>
<p>The encoding flag indicates the encoding for all string types in the packet (more on those later). Possible
values are:</p>
<table>
<thead>
<tr>
<td><code>E</code></td>
<td><code>~E</code></td>
<td colspan="3">Encoding name</td>
</tr>
</thead>
<tr>
<td><code>0x20</code></td>
<td><code>0xDF</code></td>
<td><code>ASCII</code></td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>0x40</code></td>
<td><code>0xBF</code></td>
<td><code>ISO-8859-1</code></td>
<td><code>ISO_8859-1</code></td>
<td></td>
</tr>
<tr>
<td><code>0x60</code></td>
<td><code>0x9F</code></td>
<td><code>EUC-JP</code></td>
<td><code>EUCJP</code></td>
<td><code>EUC_JP</code></td>
</tr>
<tr>
<td><code>0x80</code></td>
<td><code>0x7F</code></td>
<td><code>SHIFT-JIS</code></td>
<td><code>SHIFT_JIS</code></td>
<td><code>SJIS</code></td>
</tr>
<tr>
<td><code>0xA0</code></td>
<td><code>0x5F</code></td>
<td><code>UTF-8</code></td>
<td><code>UTF8</code></td>
<td></td>
</tr>
</table>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs.</p>
<figure>
<img src="./images/encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b960</code></figcaption>
</figure>
<p>A second table exists just before this on in the source, responsible for the
<code>&lt;?xml version='1.0' encoding='??'?&gt;</code> line in XML files.
</p>
<figure>
<img src="./images/xml_encoding_table.png">
<figcaption><code>libavs-win32.dll:0x1006b940</code></figcaption>
</figure>
<p>This is indexed using the following function, which maps the above encoding IDs to 1, 2, 3, 4 and 5
respectively.</p>
<pre><code>char* xml_get_encoding_name(uint encoding_id) {
return ENCODING_NAME_TABLE[((encoding_id & 0xe0) >> 5) * 4];
}</code></pre>
</details>
<p>While validating <code>~E</code> isn't technically required, it acts as a useful assertion that the packet being
parsed is valid.</p>
<h2 id="schema">The packet schema header</h2>
<p>Following the 4 byte header, is a 4 byte integer containing the length of the next part of the header (this is
technically made redundant as this structure is also terminated).</p>
<p>This part of the header defines the schema that the main payload uses.</p>
<p>A tag definition looks like:</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td>Type</td>
<td>nlen</td>
<td colspan="7">Tag name</td>
<td style="border-bottom: none" colspan="8"></td>
</tr>
<tr>
<td style="border-top: none;" colspan="15">Attributes and children</td>
<td colspan="1"><i>FE</i></td>
</tr>
</table>
<p>Structure names are encoded as densely packed 6 bit values, length prefixed (<code>nlen</code>). The acceptable
alphabet is <code>0123456789:ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz</code>, and the packed values
are indecies within this alphabet.</p>
<p>The children can be a combination of either attribute names, or child tags. Attribute names are represented by
the byte <code>0x2E</code> followed by a length prefixed name as defined above. Child tags follow the above
format. Type <code>0x2E</code> must therefore be considered reserved as a possible structure type.</p>
<p>Attributes (type <code>0x2E</code>) represent a string attribute. Any other attribute must be defined as a child
tag. Is it notable that 0 children is allowable, which is how the majority of values are encoded.</p>
<p>All valid IDs, and their respective type, are listed in the following table. The bucket column here will be
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.</p>
<table class="code">
<thead>
<tr>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
<td></td>
<td>ID</td>
<td>Bytes</td>
<td>C type</td>
<td>Bucket</td>
<td colspan="2">XML names</td>
</tr>
</thead>
<tr>
<td>0x01</td>
<td>0</td>
<td>void</td>
<td>-</td>
<td>void</td>
<td></td>
<td></td>
<td>0x21</td>
<td>24</td>
<td>uint64[3]</td>
<td>int</td>
<td>3u64</td>
<td></td>
</tr>
<tr>
<td>0x02</td>
<td>1</td>
<td>int8</td>
<td>byte</td>
<td>s8</td>
<td></td>
<td></td>
<td>0x22</td>
<td>12</td>
<td>float[3]</td>
<td>int</td>
<td>3f</td>
<td></td>
</tr>
<tr>
<td>0x03</td>
<td>1</td>
<td>uint8</td>
<td>byte</td>
<td>u8</td>
<td></td>
<td></td>
<td>0x23</td>
<td>24</td>
<td>double[3]</td>
<td>int</td>
<td>3d</td>
<td></td>
</tr>
<tr>
<td>0x04</td>
<td>2</td>
<td>int16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x24</td>
<td>4</td>
<td>int8[4]</td>
<td>int</td>
<td>4s8</td>
<td></td>
</tr>
<tr>
<td>0x05</td>
<td>2</td>
<td>uint16</td>
<td>short</td>
<td>s16</td>
<td></td>
<td></td>
<td>0x25</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>4u8</td>
<td></td>
</tr>
<tr>
<td>0x06</td>
<td>4</td>
<td>int32</td>
<td>int</td>
<td>s32</td>
<td></td>
<td></td>
<td>0x26</td>
<td>8</td>
<td>int16[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x07</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>u32</td>
<td></td>
<td></td>
<td>0x27</td>
<td>8</td>
<td>uint8[4]</td>
<td>int</td>
<td>4s16</td>
<td></td>
</tr>
<tr>
<td>0x08</td>
<td>8</td>
<td>int64</td>
<td>int</td>
<td>s64</td>
<td></td>
<td></td>
<td>0x28</td>
<td>16</td>
<td>int32[4]</td>
<td>int</td>
<td>4s32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x09</td>
<td>8</td>
<td>uint64</td>
<td>int</td>
<td>u64</td>
<td></td>
<td></td>
<td>0x29</td>
<td>16</td>
<td>uint32[4]</td>
<td>int</td>
<td>4u32</td>
<td>vs32</td>
</tr>
<tr>
<td>0x0a</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>bin</td>
<td>binary</td>
<td></td>
<td>0x2a</td>
<td>32</td>
<td>int64[4]</td>
<td>int</td>
<td>4s64</td>
<td></td>
</tr>
<tr>
<td>0x0b</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>str</td>
<td>string</td>
<td></td>
<td>0x2b</td>
<td>32</td>
<td>uint64[4]</td>
<td>int</td>
<td>4u64</td>
<td></td>
</tr>
<tr>
<td>0x0c</td>
<td>4</td>
<td>uint8[4]</td>
<td>int</td>
<td>ip4</td>
<td></td>
<td></td>
<td>0x2c</td>
<td>16</td>
<td>float[4]</td>
<td>int</td>
<td>4f</td>
<td>vf</td>
</tr>
<tr>
<td>0x0d</td>
<td>4</td>
<td>uint32</td>
<td>int</td>
<td>time</td>
<td></td>
<td></td>
<td>0x2d</td>
<td>32</td>
<td>double[4]</td>
<td>int</td>
<td>4d</td>
<td></td>
</tr>
<tr>
<td>0x0e</td>
<td>4</td>
<td>float</td>
<td>int</td>
<td>float</td>
<td>f</td>
<td></td>
<td>0x2e</td>
<td><i>prefix</i></td>
<td>char[]</td>
<td>int</td>
<td>attr</td>
<td></td>
</tr>
<tr>
<td>0x0f</td>
<td>8</td>
<td>double</td>
<td>int</td>
<td>double</td>
<td>d</td>
<td></td>
<td>0x2f</td>
<td>0</td>
<td></td>
<td>-</td>
<td>array</td>
<td></td>
</tr>
<tr>
<td>0x10</td>
<td>2</td>
<td>int8[2]</td>
<td>short</td>
<td>2s8</td>
<td></td>
<td></td>
<td>0x30</td>
<td>16</td>
<td>int8[16]</td>
<td>int</td>
<td>vs8</td>
<td></td>
</tr>
<tr>
<td>0x11</td>
<td>2</td>
<td>uint8[2]</td>
<td>short</td>
<td>2u8</td>
<td></td>
<td></td>
<td>0x31</td>
<td>16</td>
<td>uint8[16]</td>
<td>int</td>
<td>vu8</td>
<td></td>
</tr>
<tr>
<td>0x12</td>
<td>4</td>
<td>int16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x32</td>
<td>16</td>
<td>int8[8]</td>
<td>int</td>
<td>vs16</td>
<td></td>
</tr>
<tr>
<td>0x13</td>
<td>4</td>
<td>uint16[2]</td>
<td>int</td>
<td>2s16</td>
<td></td>
<td></td>
<td>0x33</td>
<td>16</td>
<td>uint8[8]</td>
<td>int</td>
<td>vu16</td>
<td></td>
</tr>
<tr>
<td>0x14</td>
<td>8</td>
<td>int32[2]</td>
<td>int</td>
<td>2s32</td>
<td></td>
<td></td>
<td>0x34</td>
<td>1</td>
<td>bool</td>
<td>byte</td>
<td>bool</td>
<td>b</td>
</tr>
<tr>
<td>0x15</td>
<td>8</td>
<td>uint32[2]</td>
<td>int</td>
<td>2u32</td>
<td></td>
<td></td>
<td>0x35</td>
<td>2</td>
<td>bool[2]</td>
<td>short</td>
<td>2b</td>
<td></td>
</tr>
<tr>
<td>0x16</td>
<td>16</td>
<td>int16[2]</td>
<td>int</td>
<td>2s64</td>
<td>vs64</td>
<td></td>
<td>0x36</td>
<td>3</td>
<td>bool[3]</td>
<td>int</td>
<td>3b</td>
<td></td>
</tr>
<tr>
<td>0x17</td>
<td>16</td>
<td>uint16[2]</td>
<td>int</td>
<td>2u64</td>
<td>vu64</td>
<td></td>
<td>0x37</td>
<td>4</td>
<td>bool[4]</td>
<td>int</td>
<td>4b</td>
<td></td>
</tr>
<tr>
<td>0x18</td>
<td>8</td>
<td>float[2]</td>
<td>int</td>
<td>2f</td>
<td></td>
<td></td>
<td>0x38</td>
<td>16</td>
<td>bool[16]</td>
<td>int</td>
<td>vb</td>
<td></td>
</tr>
<tr>
<td>0x19</td>
<td>16</td>
<td>double[2]</td>
<td>int</td>
<td>2d</td>
<td>vd</td>
<td></td>
<td>0x38</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1a</td>
<td>3</td>
<td>int8[3]</td>
<td>int</td>
<td>3s8</td>
<td></td>
<td></td>
<td>0x39</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1b</td>
<td>3</td>
<td>uint8[3]</td>
<td>int</td>
<td>3u8</td>
<td></td>
<td></td>
<td>0x3a</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1c</td>
<td>6</td>
<td>int16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3b</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1d</td>
<td>6</td>
<td>uint16[3]</td>
<td>int</td>
<td>3s16</td>
<td></td>
<td></td>
<td>0x3c</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1e</td>
<td>12</td>
<td>int32[3]</td>
<td>int</td>
<td>3s32</td>
<td></td>
<td></td>
<td>0x3d</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x1f</td>
<td>12</td>
<td>uint32[3]</td>
<td>int</td>
<td>3u32</td>
<td></td>
<td></td>
<td>0x3e</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<tr>
<td>0x20</td>
<td>24</td>
<td>int64[3]</td>
<td>int</td>
<td>3s64</td>
<td></td>
<td></td>
<td>0x3f</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
</table>
<p>Strings should be encoded and decoded according to the encoding specified in the packet header. Null termination is
optional, however should be stripped during decoding.</p>
<p>All of these IDs are <code>& 0x3F</code>. Any value can be turned into an array by setting the 7<sup>th</sup> bit
high (<code>| 0x40</code>). Arrays of this form, in the data section, will be an aligned <code>size: u32</code>
immediately followed by <code>size</code> bytes' worth of (unaligned!) values of the unmasked type.</p>
<details>
<summary>Source code details</summary>
<p>The full table for these values can be found in libavs. This table contains the names of every tag, along
with additional information such as how many bytes that data type requires, and which parsing function
should be used.</p>
<figure>
<img src="./images/types_table.png">
<figcaption><code>libavs-win32.dll:0x100782a8</code></figcaption>
</figure>
</details>
<details>
<summary>Note about the <code>array</code> type:</summary>
<p>While I'm not totally sure, I have a suspicion this type is used internally as a pseudo-type. Trying to
identify its function as a parsable type has some obvious blockers:</p>
<p>All of the types have convenient <code>printf</code>-using helper functions that are used to emit them when
serializing XML. All except one.</p>
<img src="./images/no_array.png">
<p>If we have a look inside the function that populates node sizes (<code>libavs-win32.dll:0x1000cf00</code>),
it has an explicit case, however is the same fallback as the default case.</p>
<img src="./images/no_array_2.png">
<p>In the same function, however, we can find a second (technically first) check for the array type.</p>
<img src="./images/yes_array.png">
<p>This seems to suggest that internally arrays are represented as a normal node, with the <code>array</code>
type, however when serializing it's converted into the array types we're used to (well, will be after the
next sections) by masking 0x40 onto the contained type.</p>
<p>Also of interest from this snippet is the fact that <code>void</code>, <code>bin</code>, <code>str</code>,
and <code>attr</code> cannot be arrays. <code>void</code> and <code>attr</code> make sense, however
<code>str</code> and <code>bin</code> are more interesting. I suspect this is because konami want to be able
to preallocate the memory, which wouldn't be possible with these variable length structures.
</p>
</details>
<h2 id="data">The data section</h2>
<p>This is where all the actual packet data is. For the most part, parsing this is the easy part. We traverse our
schema, and read values out of the packet according to the value indicated in the schema. Unfortunately, konami
decided all data should be aligned very specifically, and that gaps left during alignment should be backfilled
later. This makes both reading and writing somewhat more complicated, however the system can be fairly easily
understood.</p>
<p>Firstly, we divide the payload up into 4 byte chunks. Each chunk can be allocated to either store individual
bytes, shorts, or ints (these are the buckets in the table above). When reading or writing a value, we first
check if a chunk allocated to the desired type's bucket is available and has free/as-yet-unread space within it.
If so, we will store/read our data to/from there. If there is no such chunk, we claim the next unclaimed chunk
for our bucket.</p>
<p>For example, imagine we write the sequence <code>byte, int, byte, short, byte, int, short</code>. The final output
should look like:</p>
<table class="code">
<thead>
<tr>
<td>0</td>
<td>1</td>
<td>2</td>
<td>3</td>
<td>4</td>
<td>5</td>
<td>6</td>
<td>7</td>
<td>8</td>
<td>9</td>
<td>10</td>
<td>11</td>
<td>12</td>
<td>13</td>
<td>14</td>
<td>15</td>
</tr>
</thead>
<tr>
<td>byte</td>
<td>byte</td>
<td>byte</td>
<td></td>
<td colspan="4">int</td>
<td colspan="2">short</td>
<td colspan="2">short</td>
<td colspan="4">int</td>
</tr>
</table>
<p>While this might seem a silly system compared to just not aligning values, it is at least possible to intuit that it
helps reduce wasted space. It should be noted that any variable-length structure, such as a string or an array,
claims all chunks it encroaches on for the <code>int</code> bucket, disallowing the storage of bytes or shorts
within them.</p>
<details>
<summary>Implementing a packer</summary>
<p>While the intuitive way to understand the packing algorithm is via chunks and buckets, a far more efficient
implementation can be made that uses three pointers. Rather than try to explain in words, hopefully this python
implementation should suffice as explanation:
<pre><code>class Packer:
def __init__(self, offset=0):
self._word_cursor = offset
self._short_cursor = offset
self._byte_cursor = offset
self._boundary = offset % 4
def _next_block(self):
self._word_cursor += 4
return self._word_cursor - 4
def request_allocation(self, size):
if size == 0:
return self._word_cursor
elif size == 1:
if self._byte_cursor % 4 == self._boundary:
self._byte_cursor = self._next_block() + 1
else:
self._byte_cursor += 1
return self._byte_cursor - 1
elif size == 2:
if self._short_cursor % 4 == self._boundary:
self._short_cursor = self._next_block() + 2
else:
self._short_cursor += 2
return self._short_cursor - 2
else:
old_cursor = self._word_cursor
for _ in range(math.ceil(size / 4)):
self._word_cursor += 4
return old_cursor
def notify_skipped(self, no_bytes):
for _ in range(math.ceil(no_bytes / 4)):
self.request_allocation(4)</code></pre>
</p>
</details>
<a href="./transport.html">Prev page</a> | <a href="./protocol.html">Next page</a>
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "base.html" %}
{% block body %}
<h1><code>apsmanager</code></h1>
<h2 id="getstat"><code>apsmanager.getstat</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;apsmanager method="getstat" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;apsmanager status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,212 @@
{% extends "base.html" %}
{% block body %}
<h1><code>cardmng</code></h1>
<p>As the name might imply, this service is responsible for handling interactions with physical e-Amusement cards.
e-Amusement currently has two different types of cards in circulation. There are classic e-Amusement cards
making use of a magnetic stripe, and the newer RFID cards using FeliCa (these are probably what you have). They
are identified in requests using the <code>cardtype</code> attribute as in the below table.
</p>
<p>e-Amusement cards have a "card number" and a "card id". Confusingly, neither is a number. The card number is the
one printed on your card. The card ID is your KONAMI ID. You can (and should) read about the algorithm used for
these IDs on <a href="../cardid.html">the Card IDs page</a>.</p>
<p>In the interest of not wasting space, <code>cardid</code> and <code>cardtype</code> will be omitted from
individual breakdowns where their meaning is obvious.</p>
<h4>Card types:</h4>
<table>
<thead>
<tr>
<td><code>cardtype</code></td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>1</code></td>
<td>Old style magnetic stripe card</td>
</tr>
<tr>
<td><code>2</code></td>
<td>FeliCa RFID card</td>
</tr>
</table>
<ul>
<li><code><a href="#inquire">cardmng.inquire</a></code></li>
<li><code><a href="#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="#authpass">cardmng.authpass</a></code></li>
<li><code><a href="#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<h2 id="inquire"><code>cardmng.inquire</code></h2>
<p>Request information about a card that has been inserted or touched against a reader.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="inquire" cardid="" cardtype="" update="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>update</code></td>
<td>Should the tracked last play time be updated by this inquiry? (Just a guess)</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" newflag="" binded="" expired=" ecflag="" useridflag="" extidflag="" lastupdate="" /&gt;
&lt;/response&gt;</code></pre>
<p>To handle this request, we first must lookup if this <code>cardid</code> has ever been seen by our servers
before. If not, we abort with a <code>112</code> status. Otherwise, we proceeed to check if this card has been
seen for this specific game. If we have never seen this card used on this game, it is possible this card was
used with an older version of this game, and migration is supported, in which case we report as if we had found
a profile for this game.</p>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>newflag</code></td>
<td>Inverse of <code>binded</code></td>
</tr>
<tr>
<td><code>binded</code></td>
<td>Has a profile ever been created for this game (or an older version, requiring a migration)
(<code>1</code> or <code>0</code>)</td>
</tr>
<tr>
<td><code>expired</code></td>
<td>? Just set to <code>0</code>.</td>
</tr>
</table>
<h2 id="getrefid"><code>cardmng.getrefid</code></h2>
<p>Register a new card to this server.</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getrefid" cardtype="" cardid=" newflag="" passwd="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>newflag</code></td>
<td>?</td>
</tr>
<tr>
<td><code>passwd</code></td>
<td>The pin for this new user. <i>Should</i> always be a four digit number (and that's worth validating),
but it's passed as a string so could feasibly be anything desired.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" refid="" dataid="" pcode="" /&gt;
&lt;/response&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>A reference to this card to be used in other requests</td>
</tr>
<tr>
<td><code>dataid</code></td>
<td>Appears to be set the same as <code>refid</code>; presumably to allow different keys for game state vs
login details.</td>
</tr>
<tr>
<td><code>pcode</code></td>
<td>? Not present in captured data.</td>
</tr>
</table>
<h2 id="bindmodel"><code>cardmng.bindmodel</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindmodel" refid="" newflag="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" dataid="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="bindcard"><code>cardmng.bindcard</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="bindcard" cardtype="" newid="" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="authpass"><code>cardmng.authpass</code></h2>
<p>Test a pin for a card. This request notably uses the <code>refid</code>, so required a
<code>cardmng.inquire</code> call to be made first.
</p>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="authpass" refid="" pass="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<table>
<tr>
<td><code>refid</code></td>
<td>The reference we received either during <code>cardmng.inquire</code> or <code>cardmng.getrefid</code>
(the latter for new cards)</td>
</tr>
<tr>
<td><code>pass</code></td>
<td>The pin to test. See <code>cardmng.getrefid</code>.</td>
</tr>
</table>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<p>If the pin is valid, status should be <code>0</code>. Otherwise, <code>116</code>.</p>
<h2 id="getkeepspan"><code>cardmng.getkeepspan</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepspan" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepspan="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getkeepremain"><code>cardmng.getkeepremain</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getkeepremain" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>" keepremain="" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="getdatalist"><code>cardmng.getdatalist</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;cardmng method="getdatalist" refid="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;cardmng status="<i>status</i>"&gt;
&lt;item[]&gt;
&lt;mcode __type="str" /&gt;
&lt;dataid __type="str" /&gt;
&lt;regtime __type="str" /&gt;
&lt;lasttime __type="str" /&gt;
&lt;exptime __type="str" /&gt;
&lt;expflag __type="u8" /&gt;
&lt;/item[]&gt;
&lt;/cardmng&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,34 @@
{% extends "base.html" %}
{% block body %}
<h1><code>dlstatus</code></h1>
<h2 id="done"><code>dlstatus.done</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="done"&gt;
&lt;url&gt;
&lt;param __type="str" /&gt;
&lt;/url&gt;
&lt;name __type="str" /&gt;
&lt;size __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>"&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/response&gt;</code></pre>
<h2 id="progress"><code>dlstatus.progress</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;dlstatus method="progress" /&gt;
&lt;progress __type="s32" /&gt;
&lt;/dlstatus&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;dlstatus status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>eacoin</code></h1> <h1><code>eacoin</code></h1>
<h2 id="checkin"><code>eacoin.checkin</code></h2> <h2 id="checkin"><code>eacoin.checkin</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -197,4 +177,4 @@
&lt;/history&gt; &lt;/history&gt;
&lt;/eacoin&gt; &lt;/eacoin&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
</body> {% endblock %}

View File

@ -0,0 +1,17 @@
{% extends "base.html" %}
{% block body %}
<h1><code>esign</code></h1>
<h2 id="request"><code>esign.request</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esign method="request"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esign status="<i>status</i>"&gt;
<i>placeholder</i>
&lt;/esign&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends "base.html" %}
{% block body %}
<h1><code>esoc</code></h1>
<h2 id="read"><code>esoc.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="read"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>"&gt;
&lt;recvdata /&gt;
&lt;/esoc&gt;
&lt;/response&gt;</code></pre>
<p>Go figure.</p>
<h2 id="write"><code>esoc.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;esoc method="write"&gt;
&lt;senddata /&gt;
&lt;/esoc&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;esoc status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,38 @@
{% extends "base.html" %}
{% block body %}
<h1><code>eventlog</code></h1>
<h2 id="write"><code>eventlog.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;eventlog method="write"&gt;
&lt;retrycnt __type="u32" /&gt;
&lt;data&gt;
&lt;eventid __type="str" /&gt;
&lt;eventorder __type="s32" /&gt;
&lt;pcbtime __type="u64" /&gt;
&lt;gamesession __type="s64" /&gt;
&lt;strdata1 __type="str" /&gt;
&lt;strdata2 __type="str" /&gt;
&lt;numdata1 __type="s64" /&gt;
&lt;numdata2 __type="s64" /&gt;
&lt;locationid __type="str" /&gt;
&lt;/data&gt;
&lt;/eventlog&gt;
&lt;/call&gt;</code></pre>
<p>Event ID list:</p>
<ul>
<li><code>G_GAMED</code></li>
<li><code>S_ERROR</code></li>
<li><code>S_PWRON</code> <b>TODO: find more!</b></li>
<li><code>T_OTDEMO</code></li>
</ul>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;eventlog status="<i>status</i>"&gt;
&lt;gamesession __type="s64" /&gt;
&lt;logsendflg __type="s32" /&gt;
&lt;logerrlevel __type="s32" /&gt;
&lt;evtidnosendflg __type="s32" /&gt;
&lt;/eventlog&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>facility</code></h1> <h1><code>facility</code></h1>
<h2 id="get"><code>facility.get</code></h2> <h2 id="get"><code>facility.get</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -137,4 +117,4 @@
<p><code>region</code> is used for Japan, and has the value <code>JP-[prefecture]</code> where prefecture ranges <p><code>region</code> is used for Japan, and has the value <code>JP-[prefecture]</code> where prefecture ranges
from 1 through 47.</p> from 1 through 47.</p>
<p><b>TODO: Compile the list of regions</b></p> <p><b>TODO: Compile the list of regions</b></p>
</body> {% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="../..">Contents</a></td>
<td><a href="../../transport.html">Transport layer</a></td>
<td><a href="../../packet.html">Packet format</a></td>
<td><a href="../../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1 id="game"><code>game</code></h1> <h1 id="game"><code>game</code></h1>
<h2 id="sample"><code>game.sv4_sample</code></h2> <h2 id="sample"><code>game.sv4_sample</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -217,10 +197,10 @@
&lt;/game&gt; &lt;/game&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
<h2 id="lounce"><code>game.sv4_lounce</code></h2> <h2 id="lounge"><code>game.sv4_lounge</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt; <pre><code>&lt;call <i>...</i>&gt;
&lt;game method="sv4_lounce"&gt; &lt;game method="sv4_lounge"&gt;
<i>placeholder</i> <i>placeholder</i>
&lt;/game&gt; &lt;/game&gt;
&lt;/call&gt;</code></pre> &lt;/call&gt;</code></pre>
@ -384,4 +364,4 @@
<i>placeholder</i> <i>placeholder</i>
&lt;/game&gt; &lt;/game&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
</body> {% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>matching</code></h1> <h1><code>matching</code></h1>
<h2 id="request"><code>matching.request</code></h2> <h2 id="request"><code>matching.request</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -100,4 +80,4 @@
&lt;result __type="s32" /&gt; &lt;result __type="s32" /&gt;
&lt;/matching&gt; &lt;/matching&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
</body> {% endblock %}

View File

@ -0,0 +1,15 @@
{% extends "base.html" %}
{% block body %}
<h1><code>message</code></h1>
<h2 id="get"><code>message.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;message method="get" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;message expire="" status="<i>status</i>"&gt;
&lt;item[] name="" start="" end="" data="" /&gt;
&lt;/message&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends "base.html" %}
{% block body %}
<h1><code>package</code></h1>
<h2 id="list"><code>package.list</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="list" pkgtype="<i>pkgtype</i>" model*="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>all</code> is the only currently observed value for <code>pkgtype</code></p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>"&gt;
&lt;item[] url="" /&gt;
&lt;/package&gt;
&lt;/response&gt;</code></pre>
<p>A list of all packages available for download.</p>
<h2 id="intend"><code>package.intend</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;package method="intend" url="" model*="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;package status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,21 @@
{% extends "base.html" %}
{% block body %}
<h1><code>pcbevent</code></h1>
<h2 id="put"><code>pcbevent.put</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbevent method="put"&gt;
&lt;time __type="time" /&gt;
&lt;seq __type="u32" /&gt;
&lt;item[]&gt;
&lt;name __type="str" /&gt;
&lt;value __type="s32" /&gt;
&lt;time __type="time" /&gt;
&lt;/item[]&gt;
&lt;/pcbevent&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbevent status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,19 @@
{% extends "base.html" %}
{% block body %}
<h1><code>pcbtracker</code></h1>
<h2 id="alive"><code>pcbtracker.alive</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;pcbtracker method="alive" model*="" hardid="" softid="" accountid="" agree="" ecflag="" /&gt;
&lt;/call&gt;</code></pre>
<p><code>ecflag</code> here is determining if the arcade operator allows the use of paseli on this machine.</p>
<p><code>agree@</code> and <code>ecflag@</code> appear to either be totally non present, or present with a value of
<code>"1"</code>, but then again I may be reading the code wrong, so take that with a pinch of salt.
</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;pcbtracker status="" time="" limit="" ecenable="" eclimit="" &gt;
&lt;/response&gt;</code></pre>
<p>As you might guess, <code>ecenable@</code> is therefore the flag to determine if paseli is enabled (i.e. the
arcade operator and the server both allow its use).</p>
{% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>playerdata</code></h1> <h1><code>playerdata</code></h1>
<h2 id="usergamedata_send"><code>playerdata.usergamedata_send</code></h2> <h2 id="usergamedata_send"><code>playerdata.usergamedata_send</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -153,4 +133,4 @@
&lt;/rank&gt; &lt;/rank&gt;
&lt;/playerdata&gt; &lt;/playerdata&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
</body> {% endblock %}

View File

@ -0,0 +1,50 @@
{% extends "base.html" %}
{% block body %}
<h1><code>services</code></h1>
<h2 id="get"><code>services.get</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;services method="get" model*="" &gt;
&lt;info&gt;
&lt;AVS2 __type="str"&gt;<i>AVS2 version</i>&lt;/AVS2&gt;
&lt;/info&gt;
&lt;/services&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;services expire="" method="get" mode="" status="<i>status</i>"&gt;
&lt;item[] name="<i>service</i>" url="<i>url</i>" /&gt;
&lt;/services&gt;
&lt;/response&gt;</code></pre>
<p>Known services are:</p>
<ul>
<li><code>ntp</code></li>
<li><code>keepalive</code></li>
<li><code>cardmng</code></li>
<li><code>facility</code></li>
<li><code>message</code></li>
<li><code>numbering</code></li>
<li><code>package</code></li>
<li><code>pcbevent</code></li>
<li><code>pcbtracker</code></li>
<li><code>pkglist</code></li>
<li><code>posevent</code></li>
<li><code>userdata</code></li>
<li><code>userid</code></li>
<li><code>eacoin</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>dlstatus</code></li>
<li><code>netlog</code></li>
<li><code>sidmgr</code></li>
<li><code>globby</code></li>
</ul>
<p>Most of these will usually just return the URL to the eAmuse server (or your fake one ;D). <code>ntp</code> is a
notable exception, unless you're planning on reimplementing NTP. <code>keepalive</code> will likely alsop be a
custom URL with query parameters pre-baked.</p>
<p><code>mode</code> is one of <code>operation</code>, <code>debug</code>, <code>test</code>, or
<code>factory</code>.
</p>
{% endblock %}

View File

@ -0,0 +1,74 @@
{% extends "base.html" %}
{% block body %}
<h1><code>sidmgr</code></h1>
<h2 id="create"><code>sidmgr.create</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="create"&gt;
&lt;cardtype __type="str" /&gt;
&lt;cardid __type="str" /&gt;
&lt;cardgid __type="str" /&gt;
&lt;steal __type="u8" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;e_count __type="u8" /&gt;
&lt;last __type="time" /&gt;
&lt;locked __type="time" /&gt;
&lt;sid __type="str" /&gt;
&lt;cardid_status __type="u8" /&gt;
&lt;refid __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="open"><code>sidmgr.open</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="open" sid="" &gt;
&lt;pass __type="str" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>"&gt;
&lt;state __type="u32" /&gt;
&lt;refid __type="str" /&gt;
&lt;locked __type="time" /&gt;
&lt;/sidmgr&gt;
&lt;/response&gt;</code></pre>
<h2 id="touch"><code>sidmgr.touch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="touch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="branch"><code>sidmgr.branch</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="branch" sid="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
<h2 id="close"><code>sidmgr.close</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;sidmgr method="close" sid="" /&gt;
&lt;cause __type="u32" /&gt;
&lt;/sidmgr&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;sidmgr status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -1,25 +1,5 @@
<!DOCTYPE html> {% extends "base.html" %}
<html lang="en"> {% block body %}
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>e-Amusement API</title>
<link rel="stylesheet" href="../styles.css">
</head>
<body>
<table>
<tr>
<td><a href="..">Contents</a></td>
<td><a href="../transport.html">Transport layer</a></td>
<td><a href="../packet.html">Packet format</a></td>
<td><a href="../protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1><code>system</code></h1> <h1><code>system</code></h1>
<h2 id="getmaster"><code>system.getmaster</code></h2> <h2 id="getmaster"><code>system.getmaster</code></h2>
<h3>Request:</h3> <h3>Request:</h3>
@ -123,4 +103,4 @@
&lt;/data&gt; &lt;/data&gt;
&lt;/system&gt; &lt;/system&gt;
&lt;/response&gt;</code></pre> &lt;/response&gt;</code></pre>
</body> {% endblock %}

View File

@ -0,0 +1,20 @@
{% extends "base.html" %}
{% block body %}
<h1><code>traceroute</code></h1>
<h2 id="send"><code>traceroute.send</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;traceroute proto="" method="send"&gt;
&lt;hop[]&gt;
&lt;valid __type="bool"&gt;
&lt;addr __type="ip4"&gt;
&lt;usec __type="u64"&gt;
&lt;/hop[]&gt;
&lt;/traceroute&gt;
&lt;/call&gt;</code></pre>
<p><code>hop</code> repeats for every hop (unsurprisingly)</p>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;traceroute status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,28 @@
{% extends "base.html" %}
{% block body %}
<h1><code>userdata</code></h1>
<h2 id="read"><code>userdata.read</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="read" card*="" model*="" label="" /&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" time=""&gt;
&lt;b[] __type="" /&gt;
&lt;/userdata&gt;
&lt;/response&gt;</code></pre>
<p><code>__type</code> here can be either <code>bin</code> or <code>str</code></p>
<h2 id="write"><code>userdata.write</code></h2>
<h3>Request:</h3>
<pre><code>&lt;call <i>...</i>&gt;
&lt;userdata method="write" card="" time="" model*="" label*="" &gt;
&lt;b[] __type="str" /&gt;
&lt;/userdata&gt;
&lt;/call&gt;</code></pre>
<h3>Response:</h3>
<pre><code>&lt;response&gt;
&lt;userdata status="<i>status</i>" /&gt;
&lt;/response&gt;</code></pre>
{% endblock %}

View File

@ -0,0 +1,263 @@
{% extends "base.html" %}
{% block body %}
<h1>Application Protocol</h1>
<p>As the previous pages have eluded to (you <i>did</i> read them, didn't you?), eAmuse uses HTTP as its main way of
getting data around. This means we need an HTTP server running but, as we'll see, we don't need to think too
hard about that.</p>
<p>Every request made is a <code>POST</code> request, to <code>//&lt;model&gt;/&lt;module&gt;/&lt;method&gt;</code>,
with its body being encoded data as described in the previous sections. In addition to the
<code>X-Compress:</code> and <code>X-Eamuse-Info:</code> headers previously detailed, there is also a
<code>X-PCB-ID:</code> header. that can be set. Your machine's PCB ID uniquely defines the physical board. This
header is added in out-bound requests, and allows the server to identify you. Importantly, it's also the value
that the server uses to identify which machines are authorized to be on the network, and which are not.
</p>
<p>Every request is followed immediately by a response. Any response code other than <code>200</code> is considered
a failure.</p>
<details>
<summary>Source code details</summary>
<figure>
<img src="images/200_only.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000f8e7</code></figcaption>
</figure>
</details>
<p>All requests follow a basic format:</p>
<pre><code>&lt;call model="<i>model</i>" srcid="<i>srcid</i>" tag="<i>tag</i>"&gt;
&lt;<i>module</i> method="<i>method</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/call&gt;</code></pre>
<p>The responses follow a similar format:</p>
<pre><code>&lt;response&gt;
&lt;<i>module</i> status="<i>status</i>" <i>...attributes</i>&gt;
<i>children</i>
&lt;/<i>module</i>&gt;
&lt;/response&gt;</code></pre>
<p>With <code>"0"</code> being a successful status. Convention is to identify a specific method as
<code><i>module</i>.<i>method</i></code>, and we'll be following this convention in this document too. There are
a <i>lot</i> of possible methods, so the majority of this document is a big reference for them all. There are a
number of generic methods, and a number of game specific ones. If you haven't clocked yet, I've been working on
an SDVX 4 build for most of these pages, and each game also comes with its own set of game-specific methods.
These are namespaces under the <code>game.%s</code> module and, in the case of SDVX 4, are all
<code>game.sv4_<i>method</i></code>. I may or may not document the SDVX 4 specific methods, but I've listed them
here anyway for completeness.
</p>
<p>Paths in the XML bodies are formatted using an XPath-like syntax. That is, <code>status@/response</code> gets the
<code>status</code> attribute from <code>response</code>, and <code>response/eacoin/sequence</code> would return
that node's value.
</p>
<p><b>NOTE:</b> I am using the non-standard notation of <code>&lt;node* ...</code> and
<code>&lt;node attr*="" ...</code> to indicate that an attribute or node is not always present! Additionally, I
am going to use the notation of <code>&lt;node[]&gt;</code> to indicate that a node repeats.
</p>
<table>
<thead>
<tr>
<td>Status</td>
<td>Meaning</td>
</tr>
</thead>
<tr>
<td><code>0</code></td>
<td>Success</td>
</tr>
<tr>
<td><code>109</code></td>
<td>No profile</td>
</tr>
<tr>
<td><code>110</code></td>
<td>Not allowed</td>
</tr>
<tr>
<td><code>112</code></td>
<td>Card not found (<code>cardmng.inquire</code>)</td>
</tr>
<tr>
<td><code>116</code></td>
<td>Card pin invalid (<code>cardmng.authpass</code>)</td>
</tr>
</table>
<details>
<summary>How to reverse engineer these calls</summary>
<p>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:
</p>
<details>
<summary>Ghidra</summary>
<img src="images/eventlog_ghidra.png">
<img src="images/matching_request_ghidra.png">
</details>
<details>
<summary>IDA Pro</summary>
<img src="images/eventlog_ida.png">
<img src="images/matching_request_ida.png">
</details>
<p>I know which of these I'd rather use for reverse engineering (sorry, Ghidra)!</p>
</details>
<h2>Possible XRPC requests</h2>
<ul>
<li><code><a href="proto/eventlog.html">eventlog.%s</a></code></li>
<ul>
<li><code><a href="proto/eventlog.html#eventlog.write">eventlog.write</a></code></li>
</ul>
<li><code><a href="proto/playerdata.html">playerdata.%s</a></code></li>
<ul>
<li><code><a href="proto/playerdata.html#usergamedata_send">playerdata.usergamedata_send</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_recv">playerdata.usergamedata_recv</a></code></li>
<li><code><a href="proto/playerdata.html#usergamedata_inheritance">playerdata.usergamedata_inheritance</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_condrecv">playerdata.usergamedata_condrecv</a></code>
</li>
<li><code><a href="proto/playerdata.html#usergamedata_scorerank">playerdata.usergamedata_scorerank</a></code>
</li>
</ul>
<li><code><a href="proto/matching.html">matching.%s</a></code></li>
<ul>
<li><code><a href="proto/matching.html#request">matching.request</a></code></li>
<li><code><a href="proto/matching.html#wait">matching.wait</a></code></li>
<li><code><a href="proto/matching.html#finish">matching.finish</a></code></li>
</ul>
<li><code><a href="proto/system.html">system.%s</a></code></li>
<ul>
<li><code><a href="proto/system.html#getmaster">system.getmaster</a></code></li>
<li><code><a href="proto/system.html#getlocationiplist">system.getlocationiplist</a></code></li>
<li><code><a href="proto/system.html#xrpcproxy">system.xrpcproxy</a></code></li>
<li><code><a href="proto/system.html#convcardnumber">system.convcardnumber</a></code></li>
</ul>
<li><code><a href="proto/esoc.html">esoc.%s</a></code></li>
<ul>
<li><code><a href="proto/esoc.html#read">esoc.read</a></code></li>
<li><code><a href="proto/esoc.html#write">esoc.write</a></code></li>
</ul>
<li><code><a href="proto/cardmng.html">cardmng.%s</a></code></li>
<ul>
<li><code><a href="proto/cardmng.html#inquire">cardmng.inquire</a></code></li>
<li><code><a href="proto/cardmng.html#getrefid">cardmng.getrefid</a></code></li>
<li><code><a href="proto/cardmng.html#bindmodel">cardmng.bindmodel</a></code></li>
<li><code><a href="proto/cardmng.html#bindcard">cardmng.bindcard</a></code></li>
<li><code><a href="proto/cardmng.html#authpass">cardmng.authpass</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepspan">cardmng.getkeepspan</a></code></li>
<li><code><a href="proto/cardmng.html#getkeepremain">cardmng.getkeepremain</a></code></li>
<li><code><a href="proto/cardmng.html#getdatalist">cardmng.getdatalist</a></code></li>
</ul>
<li><code><a href="proto/esign.html">esign.%s</a></code></li>
<ul>
<li><code><a href="proto/esign.html#request">esign.request</a></code></li>
</ul>
<li><code><a href="proto/package.html">package.%s</a></code></li>
<ul>
<li><code><a href="proto/package.html#list">package.list</a></code></li>
<li><code><a href="proto/package.html#intend">package.intend</a></code></li>
</ul>
<li><code><a href="proto/userdata.html">userdata.%s</a></code></li>
<ul>
<li><code><a href="proto/userdata.html#read">userdata.read</a></code></li>
<li><code><a href="proto/userdata.html#write">userdata.write</a></code></li>
</ul>
<li><code><a href="proto/services.html">services.%s</a></code></li>
<ul>
<li><code><a href="proto/services.html#get">services.get</a></code></li>
</ul>
<li><code><a href="proto/pcbtracker.html">pcbtracker.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbtracker.html#alive">pcbtracker.alive</a></code></li>
</ul>
<li><code><a href="proto/pcbevent.html">pcbevent.%s</a></code></li>
<ul>
<li><code><a href="proto/pcbevent.html#put">pcbevent.put</a></code></li>
</ul>
<li><code><a href="proto/message.html">message.%s</a></code></li>
<ul>
<li><code><a href="proto/message.html#get">message.get</a></code></li>
</ul>
<li><code><a href="proto/facility.html">facility.%s</a></code></li>
<ul>
<li><code><a href="proto/facility.html#get">facility.get</a></code></li>
</ul>
<li><code><a href="proto/apsmanager.html">apsmanager.%s</a></code></li>
<ul>
<li><code><a href="proto/apsmanager.html#getstat">apsmanager.getstat</a></code></li>
</ul>
<li><code><a href="proto/sidmgr.html">sidmgr.%s</a></code></li>
<ul>
<li><code><a href="proto/sidmgr.html#create">sidmgr.create</a></code></li>
<li><code><a href="proto/sidmgr.html#open">sidmgr.open</a></code></li>
<li><code><a href="proto/sidmgr.html#touch">sidmgr.touch</a></code></li>
<li><code><a href="proto/sidmgr.html#branch">sidmgr.branch</a></code></li>
<li><code><a href="proto/sidmgr.html#close">sidmgr.close</a></code></li>
</ul>
<li><code><a href="proto/dlstatus.html">dlstatus.%s</a></code></li>
<ul>
<li><code><a href="proto/dlstatus.html#done">dlstatus.done</a></code></li>
<li><code><a href="proto/dlstatus.html#progress">dlstatus.progress</a></code></li>
</ul>
<li><code><a href="proto/eacoin.html">eacoin.%s</a></code></li>
<ul>
<li><code><a href="proto/eacoin.html#checkin">eacoin.checkin</a></code></li>
<li><code><a href="proto/eacoin.html#checkout">eacoin.checkout</a></code></li>
<li><code><a href="proto/eacoin.html#consume">eacoin.consume</a></code></li>
<li><code><a href="proto/eacoin.html#getbalance">eacoin.getbalance</a></code></li>
<li><code><a href="proto/eacoin.html#getecstatus">eacoin.getecstatus</a></code></li>
<li><code><a href="proto/eacoin.html#touch">eacoin.touch</a></code></li>
<li><code><a href="proto/eacoin.html#opchpass">eacoin.opchpass</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckin">eacoin.opcheckin</a></code></li>
<li><code><a href="proto/eacoin.html#opcheckout">eacoin.opcheckout</a></code></li>
<li><code><a href="proto/eacoin.html#getlog">eacoin.getlog</a></code></li>
</ul>
<li><code><a href="proto/traceroute.html">traceroute.%s</a></code></li>
<ul>
<li><code><a href="proto/traceroute.html#send">traceroute.send</a></code></li>
</ul>
<li><code><a href="proto/game/sv4.html">game.%s</a></code></li>
<ul>
<li><code><a href="proto/game/sv4.html#sample">game.sv4_sample</a></code></li>
<li><code><a href="proto/game/sv4.html#new">game.sv4_new</a></code></li>
<li><code><a href="proto/game/sv4.html#load">game.sv4_load</a></code></li>
<li><code><a href="proto/game/sv4.html#load_m">game.sv4_load_m</a></code></li>
<li><code><a href="proto/game/sv4.html#save">game.sv4_save</a></code></li>
<li><code><a href="proto/game/sv4.html#save_m">game.sv4_save_m</a></code></li>
<li><code><a href="proto/game/sv4.html#common">game.sv4_common</a></code></li>
<li><code><a href="proto/game/sv4.html#shop">game.sv4_shop</a></code></li>
<li><code><a href="proto/game/sv4.html#hiscore">game.sv4_hiscore</a></code></li>
<li><code><a href="proto/game/sv4.html#buy">game.sv4_buy</a></code></li>
<li><code><a href="proto/game/sv4.html#exception">game.sv4_exception</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_s">game.sv4_entry_s</a></code></li>
<li><code><a href="proto/game/sv4.html#entry_e">game.sv4_entry_e</a></code></li>
<li><code><a href="proto/game/sv4.html#frozen">game.sv4_frozen</a></code></li>
<li><code><a href="proto/game/sv4.html#lounge">game.sv4_lounge</a></code></li>
<li><code><a href="proto/game/sv4.html#save_e">game.sv4_save_e</a></code></li>
<li><code><a href="proto/game/sv4.html#save_pb">game.sv4_save_pb</a></code></li>
<li><code><a href="proto/game/sv4.html#save_c">game.sv4_save_c</a></code></li>
<li><code><a href="proto/game/sv4.html#play_s">game.sv4_play_s</a></code></li>
<li><code><a href="proto/game/sv4.html#play_e">game.sv4_play_e</a></code></li>
<li><code><a href="proto/game/sv4.html#serial">game.sv4_serial</a></code></li>
<li><code><a href="proto/game/sv4.html#save_fi">game.sv4_save_fi</a></code></li>
<li><code><a href="proto/game/sv4.html#print">game.sv4_print</a></code></li>
<li><code><a href="proto/game/sv4.html#print_h">game.sv4_print_h</a></code></li>
<li><code><a href="proto/game/sv4.html#load_r">game.sv4_load_r</a></code></li>
<li><code><a href="proto/game/sv4.html#save_campaign">game.sv4_save_campaign</a></code></li>
</ul>
</ul>
<b>Totally undocumented services (based on <code>services.get</code>):</b>
<ul>
<li><code>numbering</code></li>
<li><code>pkglist</code></li>
<li><code>userid</code></li>
<li><code>local</code></li>
<li><code>local2</code></li>
<li><code>lobby</code></li>
<li><code>lobby2</code></li>
<li><code>netlog</code></li>
<li><code>globby</code></li>
</ul>
<p>I'll try and figure these out in due course, promise!</p>
{% endblock %}

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
{% block body %}
<h1>Network format</h1>
<p>eAmuse packets are sent and received over HTTP (no S), with requests being in the body of <code>POST</code> requests,
and replies being in the, well, reply.</p>
<p>The packets are typically both encrypted and compressed. The compression format used is indicated by the
<code>X-Compress</code> header, and valid values are
</p>
<ul>
<li><code>none</code></li>
<li><code>lz77</code></li>
</ul>
<details>
<summary>Source code details</summary>
<figure>
<img src="./images/lz77.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000fa29</code></figcaption>
</figure>
</details>
<p>Encryption is performed <b>after</b> compression, and uses RC4. RC4 is symmetric, so decryption is performed the same
as encryption. That is, <code>packet = encrypt(compress(data))</code> and
<code>data = decompress(decrypt(data))</code>.
</p>
<h2 id="keys">Encryption keys</h2>
<p>Encryption is not performed using a single static key. Instead, each request and response has its own key that is
generated.</p>
<p>These keys are generated baesd on the <code>X-Eamuse-Info</code> header.</p>
<p>This header loosely follows the format <code>1-[0-9a-f]{8}-[0-9a-f]{4}</code>. This corresponds to
<code>[version]-[serial]-[salt]</code>. <b>TODO: Confirm this</b>
</p>
<p>Our per-packet key is then generated using <code>md5(serial | salt | KEY)</code>. Identifying <code>KEY</code> is
left as an exercise for the reader, however should not be especially challenging. <span style="color: #fff">Check
the page source if you're stuck.</span></p>
<!-- It's 69d74627d985ee2187161570d08d93b12455035b6df0d8205df5, if you were wondering. libavs-win32-ea3.dll:0x10054160 -->
<h2 id="lz77">LZ77</h2>
<p>Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte,
followed by 8 values. Each value is either a single literal byte, if the corresponding bit in the preceeding flag is
high, or is a two byte lookup into the window.</p>
<p>The lookup bytes are structured as <code>pppppppp ppppllll</code> where <code>p</code> is a 12 bit index in the
window, and <code>l</code> is a 4 bit integer that determines how many times to repeat the value located at that
index in the window.</p>
<p>The exact algorithm used for compression is not especially important, as long as it follows this format. One can
feasibly perform no compression at all, and instead insert <code>0xFF</code> every 8 bytes (starting at index 0), to
indicate that all values are literals. While obviously poor for compression, this is an easy way to test without
first implementing a compressor.</p>
<a href="./index.html">Prev page</a> | <a href="./packet.html">Next page</a>
{% endblock %}

View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Transport | eAmuse API</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<table>
<tr>
<td><a href=".">Contents</a></td>
<td><a href="./transport.html">Transport layer</a></td>
<td><a href="./packet.html">Packet format</a></td>
<td><a href="./protocol.html">Application Protocol</a></td>
</tr>
</table>
<h1>Network format</h1>
<p>eAmuse packets are sent and received over HTTP (no S), with requests being in the body of <code>POST</code> requests, and replies being in the, well, reply.</p>
<p>The packets are typically both encrypted and compressed. The compression format used is indicated by the <code>X-Compress</code> header, and valid values are</p>
<ul>
<li><code>none</code></li>
<li><code>lz77</code></li>
</ul>
<details>
<summary>Source code details</summary>
<figure>
<img src="./images/lz77.png">
<figcaption><code>libavs-win32-ea3.dll:0x1000fa29</code></figcaption>
</figure>
</details>
<p>Encryption is performed <b>after</b> compression, and uses RC4. RC4 is symmetric, so decryption is performed the same as encryption. That is, <code>packet = encrypt(compress(data))</code> and <code>data = decompress(decrypt(data))</code>.</p>
<h2 id="keys">Encryption keys</h2>
<p>Encryption is not performed using a single static key. Instead, each request and response has its own key that is generated.</p>
<p>These keys are generated baesd on the <code>X-Eamuse-Info</code> header.</p>
<p>This header loosely follows the format <code>1-[0-9a-f]{8}-[0-9a-f]{4}</code>. This corresponds to <code>[version]-[serial]-[salt]</code>. <b>TODO: Confirm this</b></p>
<p>Our per-packet key is then generated using <code>md5(serial | salt | KEY)</code>. Identifying <code>KEY</code> is left as an exercise for the reader, however should not be especially challenging. <span style="color: #fff">Check the page source if you're stuck.</span></p>
<!-- It's 69d74627d985ee2187161570d08d93b12455035b6df0d8205df5, if you were wondering. libavs-win32-ea3.dll:0x10054160 -->
<h2 id="lz77">LZ77</h2>
<p>Packets are compressed using lzss. The compressed data structure is a repeating cycle of an 8 bit flags byte, followed by 8 values. Each value is either a single literal byte, if the corresponding bit in the preceeding flag is high, or is a two byte lookup into the window.</p>
<p>The lookup bytes are structured as <code>pppppppp ppppllll</code> where <code>p</code> is a 12 bit index in the window, and <code>l</code> is a 4 bit integer that determines how many times to repeat the value located at that index in the window.</p>
<p>The exact algorithm used for compression is not especially important, as long as it follows this format. One can feasibly perform no compression at all, and instead insert <code>0xFF</code> every 8 bytes (starting at index 0), to indicate that all values are literals. While obviously poor for compression, this is an easy way to test without first implementing a compressor.</p>
<a href="./index.html">Prev page</a> | <a href="./packet.html">Next page</a>
</body>
</html>