fifa17-recon: transfer market read routes (empty-but-valid)

Add auctionhouse/trade/tradePile/watchList/marketdata routes per ENDPOINT_MAP
market §. All share the reversed IS-list body {auctionInfo:[], credits, total,
duplicateItemIdList} (shared deser 0x18013e7f0). Served EMPTY (no live listings
yet) -- empty arrays never desync the SAX reader, so freeze-safe; unlocks the
market screens vs the prior catch-all {}. GET auctionhouse merges the
FutGetAuctionCount ints (extra keys skip). POST auctionhouse = FutISStart new
tradeId; PUT relist / watch add-remove / delete = acks. tradePile precedes
/trade (prefix collision). Populating real auctions deferred to in-game test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PN5bmpDVQR1aXgefyWAt7o
This commit is contained in:
funman300
2026-08-02 17:15:41 -07:00
parent 4e89cce37d
commit 629813b580
+53
View File
@@ -183,6 +183,15 @@ ROUTES = [
# STEP 1 (wf_0bc80ab3): zero-resolve squad in MASSINFO; club stays {}.
(re.compile(G + r"/userMassInfo"), lambda m, h: (200, MASSINFO)),
(re.compile(G + r"/season"), lambda m, h: (200, {})),
# ---- transfer market / auction house (empty-but-valid; ENDPOINT_MAP market §)
# tradePile MUST precede /trade ("/tradePile" contains the "/trade" prefix).
(re.compile(G + r"/tradePile"), lambda m, h: tradepile_route(h)),
(re.compile(G + r"/trade"), lambda m, h: trade_route(h)),
(re.compile(G + r"/watchList"), lambda m, h: watchlist_route(h)),
(re.compile(G + r"/auctionhouse"), lambda m, h: auctionhouse_route(h)),
(re.compile(G + r"/marketdata"), lambda m, h: (200, {"minPrice": 150, "maxPrice": 15000})),
(re.compile(r"/ut/delete/game/[^/]+/trade"), lambda m, h: (200, {})),
(re.compile(r"/ut/delete/game/[^/]+/watchList"), lambda m, h: (200, {})),
(re.compile(G + r"/club"), lambda m, h: (200, {"itemData": STORE.items()})),
]
@@ -301,6 +310,50 @@ def credits_route(h):
}
# ---- TRANSFER MARKET / AUCTION HOUSE (ENDPOINT_MAP market §) ----------------
# Every list response shares one body: {auctionInfo:[record], credits, total,
# duplicateItemIdList} (shared deser 0x18013e7f0). auctionInfo + dupIdList MUST be
# ARRAYS and each record.itemData MUST be a card OBJECT, or the SAX reader desyncs
# -> busy-loop freeze at 0x1801c7f1a. We serve an EMPTY-but-valid market (no live
# listings yet): empty arrays never desync, so this is freeze-safe. Populating real
# auctions needs an in-game test pass. Extra keys are SKIP'd, so one merged body
# safely satisfies both the search parser and the auction-count parser.
def _market_body():
return {"auctionInfo": [], "credits": STORE.coins(), "total": 0,
"duplicateItemIdList": []}
def auctionhouse_route(h):
# GET = search (empty results) OR count -> merged body (extra keys skip)
# POST = FutISStart (list item for sale) -> {"id": new tradeId}
# PUT = .../relist (relist all expired) -> ack {}
if h.command == "POST":
return 200, {"id": STORE.new_item_id()}
if h.command == "PUT":
return 200, {}
body = _market_body()
body.update({"count": 0, "maxAuctionsAllowed": 100,
"offered": 0, "selling": 0, "sold": 0}) # FutGetAuctionCount ints
return 200, body
def trade_route(h):
# GET view one auction / POST place bid -> {auctionInfo:[record], credits}
return 200, {"auctionInfo": [], "credits": STORE.coins()}
def tradepile_route(h):
b = _market_body(); b.pop("duplicateItemIdList", None)
return 200, b # {auctionInfo, credits, total}
def watchlist_route(h):
if h.command in ("PUT", "POST", "DELETE"):
return 200, {} # add/remove watch -> ack
b = _market_body(); b.pop("duplicateItemIdList", None)
return 200, b
class H(http.server.BaseHTTPRequestHandler):
protocol_version = "HTTP/1.1"