diff --git a/.gitignore b/.gitignore
index 25822ce..330ab9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -102,8 +102,8 @@ ENV/
# mypy
.mypy_cache/
-# logs
-logs/
+# project related things
+/logs/
+/decompiled/
+/database/
-# Decompiled v8 code
-decompiled/
diff --git a/setup.py b/setup.py
index e10f782..ea5defa 100644
--- a/setup.py
+++ b/setup.py
@@ -9,6 +9,7 @@ DOCS_DEPS = ["sphinx", "sphinx-rtd-theme", "sphinx-autoapi", "recommonmark"]
CHECK_DEPS = ["isort", "flake8", "flake8-quotes", "pep8-naming", "mypy", "black"]
REQUIREMENTS = [
"flask",
+ "flask_sqlalchemy",
"watchdog",
"pyopenssl",
"lxml",
diff --git a/v8_server/__init__.py b/v8_server/__init__.py
index 6c3a479..1e70a2f 100644
--- a/v8_server/__init__.py
+++ b/v8_server/__init__.py
@@ -5,20 +5,14 @@ from pathlib import Path
from typing import Optional, Union
from flask import Flask, has_request_context, request
+from flask_sqlalchemy import SQLAlchemy
from v8_server.config import Development, Production
-from v8_server.model.connection import Base, Database
-from v8_server.model.user import User
from v8_server.utils.flask import generate_secret_key
from .version import __version__
-# Make sure the database has been created
-with Database() as db:
- Base.metadata.create_all(db.engine)
-
-
class RequestFormatter(logging.Formatter):
def format(self, record):
encrypted = "None"
@@ -117,9 +111,13 @@ static_dir = str(package_dir / "static")
app = Flask(__name__, template_folder=template_dir, static_folder=static_dir)
app.secret_key = generate_secret_key(config.SECRET_KEY_FILENAME)
app.config.from_object(config)
+db = SQLAlchemy(app)
+
+# Make sure the database has been created
+db.create_all()
# We need to import the views here specifically once the flask app has been initialized
import v8_server.view # noqa: F401, E402
-__all__ = ["__version__", "app", "LOG_PATH"]
+__all__ = ["__version__", "app", "db", "LOG_PATH"]
diff --git a/v8_server/config.py b/v8_server/config.py
index 75bf245..38deac6 100644
--- a/v8_server/config.py
+++ b/v8_server/config.py
@@ -1,13 +1,23 @@
+from pathlib import Path
+
+
+DEV_DB_PATH = Path(__file__).parent.parent / "database"
+PROD_DB_PATH = Path("/var/db")
+
+
class Config(object):
DEBUG: bool = False
TESTING: bool = False
DB_SERVER: str = "localhost"
SECRET_KEY_FILENAME: str = "v8_server.key"
+ SQLALCHEMY_DATABASE_URI: str = f"sqlite+pysqlite:///{ PROD_DB_PATH / 'v8.db'}"
+ SQLALCHEMY_TRACK_MODIFICATIONS = False
class Development(Config):
DEBUG: bool = True
SECRET_KEY_FILENAME: str = "dev_v8_server.key"
+ SQLALCHEMY_DATABASE_URI: str = f"sqlite+pysqlite:///{ DEV_DB_PATH / 'v8_dev.db'}"
class Production(Config):
diff --git a/v8_server/eamuse/services/__init__.py b/v8_server/eamuse/services/__init__.py
index b62614d..f46bcb6 100644
--- a/v8_server/eamuse/services/__init__.py
+++ b/v8_server/eamuse/services/__init__.py
@@ -1,3 +1,4 @@
+from v8_server.eamuse.services.cardmng import CardMng
from v8_server.eamuse.services.facility import Facility
from v8_server.eamuse.services.local import Local
from v8_server.eamuse.services.message import Message
@@ -8,6 +9,7 @@ from v8_server.eamuse.services.services import ServiceRequest, Services, Service
__all__ = [
+ "CardMng",
"Facility",
"Local",
"Message",
diff --git a/v8_server/eamuse/services/cardmng.py b/v8_server/eamuse/services/cardmng.py
new file mode 100644
index 0000000..3fd0d48
--- /dev/null
+++ b/v8_server/eamuse/services/cardmng.py
@@ -0,0 +1,165 @@
+from lxml.builder import E
+
+from v8_server import db
+from v8_server.eamuse.services.services import ServiceRequest
+from v8_server.eamuse.utils.xml import get_xml_attrib
+from v8_server.model.user import Card, Profile, RefID, User
+
+
+class CardMng(object):
+ """
+ Handle the CardMng (Card Manage) request.
+
+ This is for supporting eAmuse card interaction.
+ """
+
+ # List of statuses we return to the game for various card related reasons
+ SUCCESS = 0
+ NO_PROFILE = 109
+ NOT_ALLOWED = 110
+ NOT_REGISTERED = 112
+ INVALID_PIN = 116
+
+ # Methods
+ INQUIRE = "inquire"
+ GETREFID = "getrefid"
+ AUTHPASS = "authpass"
+ BINDMODEL = "bindmodel"
+ GETKEEPSPAN = "getkeepspan"
+ GETDATALIST = "getdatalist"
+
+ @classmethod
+ def inquire(cls, req: ServiceRequest):
+ """
+ Example Request:
+
+
+
+
+ Example Response:
+
+
+
+ """
+ # Grab the card id
+ cardid = get_xml_attrib(req.xml[0], "cardid")
+
+ # Check if a user with this card id already exists
+ user = User.from_cardid(cardid)
+
+ if user is None:
+ # The user doesn't exist, force system to create a new account
+ response = E.response(E.cardmng({"status": str(CardMng.NOT_REGISTERED)}))
+ else:
+ refid = RefID.from_userid(user.userid)
+ bound = Profile.from_userid(user.userid) is not None
+
+ if refid is None:
+ raise Exception("RefID Should not be None here!")
+
+ response = E.response(
+ E.cardmng(
+ {
+ "refid": refid.refid,
+ "dataid": refid.refid,
+ "newflag": "1",
+ "binded": "1" if bound else "0",
+ "expired": "0",
+ "exflag": "0",
+ "useridflag": "1",
+ "extidflag": "1",
+ }
+ )
+ )
+
+ return response
+
+ @classmethod
+ def getrefid(cls, req: ServiceRequest):
+ """"""
+ # Grab the card id and pin
+ cardid = get_xml_attrib(req.xml[0], "cardid")
+ pin = get_xml_attrib(req.xml[0], "passwd")
+
+ # Create a new user object with the given pin
+ user = User(pin=pin)
+ db.session.add(user)
+ db.session.commit()
+
+ # Create the card, tie it to the user account
+ card = Card(cardid=cardid, userid=user.userid)
+ db.session.add(card)
+ db.session.commit()
+
+ # Generate the refid and return it
+ refid = RefID.create_with_userid(user.userid)
+
+ return E.response(E.cardmng({"dataid": refid.refid, "refid": refid.refid}))
+
+ @classmethod
+ def authpass(cls, req: ServiceRequest):
+ """"""
+ # Grab the refid and pin
+ refid_str = get_xml_attrib(req.xml[0], "refid")
+ pin = get_xml_attrib(req.xml[0], "pass")
+
+ # Grab the refid
+ refid = db.session.query(RefID).filter(RefID.refid == refid_str).one_or_none()
+
+ if refid is None:
+ raise Exception("RefID Is None Here!")
+
+ # Check if the pin is valid for the user
+ user = refid.user
+ valid = user.pin == pin
+
+ return E.response(
+ E.cardmng(
+ {"status": str(CardMng.SUCCESS if valid else CardMng.INVALID_PIN)}
+ )
+ )
+
+ @classmethod
+ def bindmodel(cls, req: ServiceRequest):
+ """"""
+ # Grab the refid
+ refid_str = get_xml_attrib(req.xml[0], "refid")
+ refid = db.session.query(RefID).filter(RefID.refid == refid_str).one_or_none()
+
+ if refid is None:
+ raise Exception("RefID is None Here!")
+
+ # Just bind some garbage here for now
+ profile = Profile(refid=refid.refid, data={"data": "something"})
+ db.session.add(profile)
+ db.session.commit()
+
+ return E.response(E.cardmng({"dataid": refid.refid}))
+
+ @classmethod
+ def getkeepspan(cls):
+ """
+ Unclear what this method does, return an arbitrary span
+ """
+ return E.response(E.cardmng({"keepspan": "30"}))
+
+ @classmethod
+ def getdatalist(cls):
+ """
+ Unclear what this method does, return a dummy response
+ """
+ return E.response(E.cardmng())
diff --git a/v8_server/eamuse/services/local.py b/v8_server/eamuse/services/local.py
index 64f12bd..9a5fe5a 100644
--- a/v8_server/eamuse/services/local.py
+++ b/v8_server/eamuse/services/local.py
@@ -22,6 +22,9 @@ class Local(object):
CARDUTIL = "cardutil"
CARDUTIL_CHECK = "check"
+ GAMEINFO = "gameinfo"
+ GAMEINFO_GET = "get"
+
@classmethod
def shopinfo(cls, req: ServiceRequest) -> etree:
"""
@@ -125,7 +128,26 @@ class Local(object):
# TODO: Figure out what this thing actually needs to send back
if req.method == cls.DEMODATA_GET:
- response = E.response(E.demodata({"expire": "600"}))
+ # response = E.response(E.demodata({"expire": "600"}))
+
+ # try some dummy response that might have some info in it
+ response = E.response(
+ E.demodata(
+ E.hitchart({"nr": "3"}),
+ E.data(
+ E.musicid("133", e_type(T.s32)),
+ E.last1("1", e_type(T.s32)),
+ ),
+ E.data(
+ E.musicid("208", e_type(T.s32)),
+ E.last1("2", e_type(T.s32)),
+ ),
+ E.data(
+ E.musicid("209", e_type(T.s32)),
+ E.last1("3", e_type(T.s32)),
+ ),
+ )
+ )
else:
raise Exception(
"Not sure how to handle this demodata request. "
@@ -153,7 +175,7 @@ class Local(object):
-
+ 0
@@ -171,3 +193,35 @@ class Local(object):
)
return response
+
+ @classmethod
+ def gameinfo(cls, req: ServiceRequest) -> etree:
+ """
+ Handle a Gameinfo request.
+
+ Currently unsure how to handle this, so we just return a dummy object.
+
+ # Example Request:
+
+
+
+ US-123
+ 1
+
+
+
+
+ Example Response:
+
+
+
+ """
+ if req.method == cls.GAMEINFO_GET:
+ response = E.response(E.gameinfo())
+ else:
+ raise Exception(
+ "Not sure how to handle this gameinfo request. "
+ f'method "{req.method}" is unknown for request: {req}'
+ )
+
+ return response
diff --git a/v8_server/eamuse/services/services.py b/v8_server/eamuse/services/services.py
index 6a8e36c..126f58b 100644
--- a/v8_server/eamuse/services/services.py
+++ b/v8_server/eamuse/services/services.py
@@ -36,6 +36,23 @@ class ServiceType(IntEnum):
CARDMNG = 6
LOCAL = 7
+ # Extra for testing
+ # BINARY = 8
+ # DLSTATUS = 9
+ # EACOIN = 10
+ # EEMALL = 11
+ # INFO = 12
+ # LOBBY = 13
+ # NETLOG = 14
+ # NUMBERING = 15
+ # PKGLIST = 16
+ # POSEVENT = 17
+ # REFERENCE = 18
+ # SHOPINF = 19
+ # SIDMGR = 20
+ # USERDATA = 21
+ # USERID = 22
+
class Services(object):
"""
diff --git a/v8_server/eamuse/utils/arc.py b/v8_server/eamuse/utils/arc.py
new file mode 100644
index 0000000..2108f49
--- /dev/null
+++ b/v8_server/eamuse/utils/arc.py
@@ -0,0 +1,57 @@
+import struct
+from typing import Dict, List, Tuple
+
+from v8_server.eamuse.utils.lz77 import Lz77
+
+
+class ARC:
+ """
+ Class representing an `.arc` file. These are found in DDR Ace, and possibly
+ other games that use ESS. Given a serires of bytes, this will allow you to
+ query included filenames as well as read the contents of any file inside the
+ archive.
+ """
+
+ def __init__(self, data: bytes) -> None:
+ self.__files: Dict[str, Tuple[int, int, int]] = {}
+ self.__data = data
+ self.__parse_file(data)
+
+ def __parse_file(self, data: bytes) -> None:
+ # Check file header
+ if data[0:4] != bytes([0x20, 0x11, 0x75, 0x19]):
+ raise Exception("Unknown file format!")
+
+ # Grab header offsets
+ (_, numfiles, _) = struct.unpack(" List[str]:
+ return [f for f in self.__files]
+
+ def read_file(self, filename: str) -> bytes:
+ (fileoffset, uncompressedsize, compressedsize) = self.__files[filename]
+
+ if compressedsize == uncompressedsize:
+ # Just stored
+ return self.__data[fileoffset : (fileoffset + compressedsize)]
+ else:
+ # Compressed
+ lz77 = Lz77()
+ return lz77.decompress(
+ self.__data[fileoffset : (fileoffset + compressedsize)]
+ )
diff --git a/v8_server/model/connection.py b/v8_server/model/connection.py
deleted file mode 100644
index 0693e62..0000000
--- a/v8_server/model/connection.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from __future__ import annotations
-
-from pathlib import Path
-from types import TracebackType
-from typing import Optional, Type
-
-from sqlalchemy import create_engine
-from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
-from sqlalchemy.orm import Session as AlchemySession
-
-
-Base: DeclarativeMeta = declarative_base()
-
-
-class Database(object):
- def __init__(self, echo: bool = False) -> None:
- self.uri = f"sqlite+pysqlite:///{Path(__file__).parent / 'v8.db'}"
- self.engine = create_engine(self.uri, echo=echo)
- self.session = AlchemySession(bind=self.engine)
-
- def __enter__(self) -> Database:
- return self
-
- def __exit__(
- self,
- exception_type: Optional[Type[BaseException]],
- exception_value: Optional[BaseException],
- traceback: Optional[TracebackType],
- ) -> bool:
- self.session.close()
- return exception_type is None
-
- def __del__(self) -> None:
- self.engine.dispose()
diff --git a/v8_server/model/user.py b/v8_server/model/user.py
index 3b8e13f..8ad71ea 100644
--- a/v8_server/model/user.py
+++ b/v8_server/model/user.py
@@ -1,11 +1,23 @@
+from __future__ import annotations
+
+import random
+from typing import Optional
+
+from flask_sqlalchemy.model import DefaultMeta
from sqlalchemy import JSON, Column, ForeignKey, UniqueConstraint
from sqlalchemy.orm import relationship
from sqlalchemy.types import Integer, String
-from v8_server.model.connection import Base
+from v8_server import db
-class User(Base):
+BaseModel: DefaultMeta = db.Model
+
+DEFAULT_GAME = "GFDM"
+DEFAULT_VERSION = "v8"
+
+
+class User(BaseModel):
"""
Table representing a user. Each user has a Unique ID and a pin which is used
with all cards associated with the user's account.
@@ -13,17 +25,22 @@ class User(Base):
__tablename__ = "users"
- uid = Column(Integer, nullable=False, primary_key=True)
+ userid = Column(Integer, nullable=False, primary_key=True)
pin = Column(String(4), nullable=False)
cards = relationship("Card", back_populates="user")
extids = relationship("ExtID", back_populates="user")
refids = relationship("RefID", back_populates="user")
def __repr__(self) -> str:
- return f'User'
+ return f'User'
+
+ @classmethod
+ def from_cardid(cls, cardid: str) -> Optional[User]:
+ card = db.session.query(Card).filter(Card.cardid == cardid).one_or_none()
+ return card.user if card is not None else None
-class Card(Base):
+class Card(BaseModel):
"""
Table representing a card associated with a user. Users may have zero or more cards
associated with them. When a new card is used in a game, a new user will be created
@@ -32,32 +49,56 @@ class Card(Base):
__tablename__ = "cards"
- uid = Column(String(16), nullable=False, primary_key=True)
- user_id = Column(Integer, ForeignKey("users.uid"), nullable=False)
+ cardid = Column(String(16), nullable=False, primary_key=True)
+ userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
user = relationship("User", back_populates="cards")
def __repr__(self) -> str:
- return f'Card'
+ return f'Card'
-class ExtID(Base):
+class ExtID(BaseModel):
"""
Table representing and extid for a user across a game series. Each game series on
the network gets its own extid (8 digit number) for each user.
"""
__tablename__ = "extids"
- __table_args_ = (UniqueConstraint("game", "user_id", name="game_user_id"),)
- uid = Column(Integer, nullable=False, primary_key=True)
+ __table_args_ = (UniqueConstraint("game", "userid", name="game_userid"),)
+ extid = Column(Integer, nullable=False, primary_key=True)
game = Column(String(32), nullable=False)
- user_id = Column(Integer, ForeignKey("users.uid"), nullable=False)
+ userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
user = relationship("User", back_populates="extids")
def __repr__(self) -> str:
- return f'ExtID'
+ return f'ExtID'
+
+ @classmethod
+ def create_with_userid(cls, userid: int) -> Optional[ExtID]:
+ # First check if this user has an ExtID for GFDM
+ extid = (
+ db.session.query(ExtID)
+ .filter(ExtID.userid == userid, ExtID.game == DEFAULT_GAME)
+ .one_or_none()
+ )
+
+ if extid is None:
+ # Create a new ExtID that is unique
+ while True:
+ extid_val = random.randint(0, 89999999) + 10000000
+ count = db.session.query(ExtID).filter(ExtID.extid == extid_val).count()
+ if count == 0:
+ break
+
+ # Use this ExtID
+ extid = ExtID(extid=extid_val, game=DEFAULT_GAME, userid=userid)
+ db.session.add(extid)
+ db.session.commit()
+
+ return extid
-class RefID(Base):
+class RefID(BaseModel):
"""
Table representing a refid for a user. Each unique game on the network will need
a refid for each user/game/version they have a profile for. If a user does not have
@@ -70,28 +111,81 @@ class RefID(Base):
__tablename__ = "refids"
__table_args__ = (
- UniqueConstraint("game", "version", "user_id", name="game_version_user_id"),
+ UniqueConstraint("game", "version", "userid", name="game_version_userid"),
)
- uid = Column(String(16), nullable=False, primary_key=True)
+ refid = Column(String(16), nullable=False, primary_key=True)
game = Column(String(32), nullable=False)
version = Column(Integer, nullable=False)
- user_id = Column(Integer, ForeignKey("users.uid"), nullable=False)
+ userid = Column(Integer, ForeignKey("users.userid"), nullable=False)
user = relationship("User", back_populates="refids")
def __repr__(self) -> str:
return (
- f'RefID"
+ f'RefID"
)
+ @classmethod
+ def from_userid(cls, userid: int) -> Optional[RefID]:
+ refid = (
+ db.session.query(RefID)
+ .filter(
+ RefID.userid == userid,
+ RefID.game == DEFAULT_GAME,
+ RefID.version == DEFAULT_VERSION,
+ )
+ .one_or_none()
+ )
-class Profile(Base):
+ return refid
+
+ @classmethod
+ def create_with_userid(cls, userid: int) -> RefID:
+ # Create the ExtID
+ # This method will return an already existing ExtID or create a new one and
+ # return it. In this case we don't care what it returns
+ _ = ExtID.create_with_userid(userid)
+
+ # Create a new RefID that is unique
+ while True:
+ refid_val = "".join(random.choice("0123456789ABCDEF") for _ in range(16))
+ count = db.session.query(RefID).filter(RefID.refid == refid_val).count()
+ if count == 0:
+ break
+
+ # Use our newly created RefID
+ refid = RefID(
+ refid=refid_val, game=DEFAULT_GAME, version=DEFAULT_VERSION, userid=userid
+ )
+ db.session.add(refid)
+ db.session.commit()
+
+ return refid
+
+
+class Profile(BaseModel):
"""
Table for storing JSON profile blobs, indexed by refid
"""
__tablename__ = "profiles"
- ref_id = Column(
- String(16), ForeignKey("refids.uid"), nullable=False, primary_key=True
+ refid = Column(
+ String(16), ForeignKey("refids.refid"), nullable=False, primary_key=True
)
data = Column(JSON, nullable=False)
+
+ @classmethod
+ def from_refid(cls, refid: str) -> Optional[Profile]:
+ return db.session.query(Profile).filter(Profile.refid == refid).one_or_none()
+
+ @classmethod
+ def from_userid(cls, userid: int) -> Optional[Profile]:
+ """
+ Returns a user profile if it exists, or None if it doesn't
+ """
+ refid = RefID.from_userid(userid)
+
+ if refid is None:
+ return None
+
+ return Profile.from_refid(refid.refid)
diff --git a/v8_server/model/v8.db b/v8_server/model/v8.db
deleted file mode 100644
index 0a43d3b..0000000
Binary files a/v8_server/model/v8.db and /dev/null differ
diff --git a/v8_server/view/index.py b/v8_server/view/index.py
index ad99a68..3780eb8 100644
--- a/v8_server/view/index.py
+++ b/v8_server/view/index.py
@@ -6,6 +6,7 @@ from sqlalchemy.orm.exc import MultipleResultsFound
from v8_server import app
from v8_server.eamuse.services import (
+ CardMng,
Facility,
Local,
Message,
@@ -17,7 +18,6 @@ from v8_server.eamuse.services import (
ServiceType,
)
from v8_server.eamuse.utils.xml import get_xml_attrib
-from v8_server.model.connection import Database
from v8_server.model.user import Card, ExtID, Profile, RefID, User
@@ -111,184 +111,26 @@ def facility_service() -> FlaskResponse:
return req.response(response)
-'''
-class CardStatus(object):
- """
- List of statuses we return to the game for various reasons
- """
-
- SUCCESS = 0
- NO_PROFILE = 109
- NOT_ALLOWED = 110
- NOT_REGISTERED = 112
- INVALID_PIN = 116
-
-
-def create_refid(user_id: int) -> str:
- with Database() as db:
- # Create a new extid that is unique
- while True:
- e_id = random.randint(0, 89999999) + 10000000
- if db.session.query(ExtID).filter(ExtID.uid == e_id).count() == 0:
- break
-
- # Use that ext_id
- ext_id = ExtID(uid=e_id, game="GFDM", user_id=user_id)
-
- try:
- db.session.add(ext_id)
- except Exception:
- # Most likely a duplicate error as this user already has an ExtID for this
- # game series
- pass
-
- # Create a new refid that is unique
- while True:
- r_id = "".join(random.choice("0123456789ABCDEF") for _ in range(16))
- if db.session.query(RefID).filter(RefID.uid == r_id).count() == 0:
- break
-
- # Use that ref_id
- ref_id = RefID(uid=r_id, game="GFDM", version=8, user_id=user_id)
- db.session.add(ref_id)
- db.session.commit()
-
- uid = ref_id.uid
-
- return uid
-
-
-def has_profile(user_id: int) -> bool:
- result = False
- with Database() as db:
- ref_id = (
- db.session.query(RefID)
- .filter(RefID.game == "GFDM", RefID.version == 8, RefID.user_id == user_id)
- .one()
- )
- result = (
- db.session.query(Profile).filter(Profile.ref_id == ref_id.uid).count() != 0
- )
- return result
-
-
-@app.route("/cardmng/service", methods=["POST"])
+@Services.route(ServiceType.CARDMNG)
def cardmng() -> Tuple[bytes, Dict[str, str]]:
- """
- This is for dealing with the card management
- """
- xml, model_str, module, method, command = eamuse_read_xml(request)
+ req = ServiceRequest(request)
- if method == "inquire":
- card_id = get_xml_attrib(xml[0], "cardid")
-
- with Database() as db:
- # Check if the user already exists
- try:
- card = db.session.query(Card).filter(Card.uid == card_id).one_or_none()
- except MultipleResultsFound:
- app.logger.error(f"Multiple Cards found for Card ID: {card_id}")
- raise
-
- if card is None:
- # This user doesn't exist, force the system to create a new account
- response = E.response(
- E.cardmng({"status": str(CardStatus.NOT_REGISTERED)})
- )
- else:
- # Special handing for looking up whether the previous game's profile
- # existed
- user = db.session.query(User).filter(User.uid == card.user_id).one()
- bound = has_profile(user.uid)
- expired = False
-
- ref_id = (
- db.session.query(RefID)
- .filter(
- RefID.game == "GFDM",
- RefID.version == 8,
- RefID.user_id == user.uid,
- )
- .one()
- )
- paseli_enabled = False
-
- response = E.response(
- E.cardmng(
- {
- "refid": ref_id.uid,
- "dataid": ref_id.uid,
- "newflag": "1", # A;ways seems to be set to 1
- "binded": "1" if bound else "0",
- "expired": "1" if expired else "0",
- "ecflag": "1" if paseli_enabled else "0",
- "useridflag": "1",
- "extidflag": "1",
- }
- )
- )
- elif method == "getrefid":
- # Given a card_id, and a pin, register the card with the system and generate a
- # new data_id/ref_id + ext_id
- card_id = get_xml_attrib(xml[0], "cardid")
- pin = get_xml_attrib(xml[0], "passwd")
-
- with Database() as db:
- # Create the user object
- user = User(pin=pin)
- db.session.add(user)
-
- # We must commit to assign a uid
- db.session.commit()
- user_id = user.uid
-
- # Now insert the card, tying it to the account
- card = Card(uid=card_id, user_id=user_id)
- db.session.add(card)
- db.session.commit()
-
- ref_id = create_refid(user_id)
- response = E.response(E.cardmng({"dataid": ref_id, "refid": ref_id}))
- elif method == "authpass":
- # Given a data_id/ref_id previously found via inquire, verify the pin
- ref_id = get_xml_attrib(xml[0], "refid")
- pin = get_xml_attrib(xml[0], "pass")
-
- with Database() as db:
- refid = db.session.query(RefID).filter(RefID.uid == ref_id).one()
- user = (
- db.session.query(User).filter(User.uid == refid.user_id).one_or_none()
- )
-
- if user is not None:
- valid = pin == user.pin
- else:
- valid = False
-
- response = E.response(
- E.cardmng(
- {
- "status": str(
- CardStatus.SUCCESS if valid else CardStatus.INVALID_PIN
- )
- }
- )
- )
- elif method == "bindmodel":
- # Given a refid, bind the user's card to the current version of the game
- # TODO: Not implemented right now. do it later
- ref_id = get_xml_attrib(xml[0], "refid")
- response = E.response(E.cardmng({"dataid": ref_id}))
- elif method == "getkeepspan":
- # Unclear what this method does, return an arbitrary span
- response = E.response(E.cardmng({"keepspan", "30"}))
- elif method == "getdatalist":
- # Unclear what this method does, return a dummy response
- response = base_response(module)
+ if req.method == CardMng.INQUIRE:
+ response = CardMng.inquire(req)
+ elif req.method == CardMng.GETREFID:
+ response = CardMng.getrefid(req)
+ elif req.method == CardMng.AUTHPASS:
+ response = CardMng.authpass(req)
+ elif req.method == CardMng.BINDMODEL:
+ response = CardMng.bindmodel(req)
+ elif req.method == CardMng.GETKEEPSPAN:
+ response = CardMng.getkeepspan()
+ elif req.method == CardMng.GETDATALIST:
+ response = CardMng.getdatalist()
else:
- response = base_response(module)
- return eamuse_prepare_xml(response, request)
-'''
+ raise Exception(f"Not sure how to handle this Cardmng Request: {req}")
+
+ return req.response(response)
@Services.route(ServiceType.LOCAL)
@@ -301,6 +143,8 @@ def local_service() -> FlaskResponse:
response = Local.demodata(req)
elif req.module == Local.CARDUTIL:
response = Local.cardutil(req)
+ elif req.module == Local.GAMEINFO:
+ response = Local.gameinfo(req)
else:
raise Exception(f"Not sure how to handle this Local Request: {req}")
return req.response(response)