diff --git a/d2warehouse/app/__init__.py b/d2warehouse/app/__init__.py new file mode 100644 index 0000000..dd12c51 --- /dev/null +++ b/d2warehouse/app/__init__.py @@ -0,0 +1 @@ +from d2warehouse.app.main import app diff --git a/d2warehouse/app/db.py b/d2warehouse/app/db.py new file mode 100644 index 0000000..e8719c3 --- /dev/null +++ b/d2warehouse/app/db.py @@ -0,0 +1,22 @@ +import sqlite3 +from flask import g +import d2warehouse.db as base_db + + +def get_db(): + if "db" not in g: + print("\n==========\nDB PATH", base_db._path, "\n============\n") + g.db = sqlite3.connect( + base_db._path, + detect_types=sqlite3.PARSE_DECLTYPES, + ) + g.db.row_factory = sqlite3.Row + + return g.db + + +def close_db(e=None): + db = g.pop("db", None) + + if db is not None: + db.close() diff --git a/d2warehouse/app/main.py b/d2warehouse/app/main.py new file mode 100644 index 0000000..f717bca --- /dev/null +++ b/d2warehouse/app/main.py @@ -0,0 +1,88 @@ +from flask import Flask, redirect, abort, render_template, request +from pathlib import Path +from d2warehouse.parser import parse_stash +import d2warehouse.db as base_db +from d2warehouse.app.db import get_db, close_db +import os +import re + +STASH_FILES = { + "softcore": "SharedStashSoftCoreV2.d2i", + "hardcore": "SharedStashHardCoreV2.d2i", +} + + +def save_path() -> Path: + if "D2SAVE_PATH" in os.environ: + path = Path(os.environ["D2SAVE_PATH"]) + else: + path = Path.home() / "Saved Games/Diablo II Resurrected" + + if not path.exists(): + raise RuntimeError("Save path `{path}` does not exist") + return path + + +base_db.set_db_path(str(save_path() / "d2warehouse.sqlite3")) +base_db.init_db() +base_db.close_db() + +app = Flask(__name__) +app.teardown_appcontext(close_db) + + +@app.route("/") +def home(): + return redirect("/stash/softcore", code=302) + + +@app.route("/stash/") +def list_stash(stash_name: str): + if stash_name not in STASH_FILES: + abort(404) + path = save_path() / STASH_FILES[stash_name] + stash_data = path.read_bytes() + stash = parse_stash(stash_data) + return render_template("list_stash.html", stash_name=stash_name, stash=stash) + + +@app.route("/stash//remove", methods=["POST"]) +def stash_remove_items(stash_name: str): + if stash_name not in STASH_FILES: + abort(404) + stash_path = save_path() / STASH_FILES[stash_name] + tmp_path = save_path() / f"{STASH_FILES[stash_name]}.temp" + if tmp_path.exists(): + # TODO: Handle this condition + return "temp file exists (BAD)" + return 500 + + stash_data = stash_path.read_bytes() + stash = parse_stash(stash_data) + + items = [] + for item_location in request.form.keys(): + match = re.match(r"(\d+)_(\d+)", item_location) + if not match: + # TODO: Handle this condition + return "invalid position" + tab_idx, item_idx = int(match.group(1)), int(match.group(2)) + if tab_idx > len(stash.tabs) or item_idx > len(stash.tabs[tab_idx].items): + # TODO: Handle this condition + return "invalid position (2)" + item = stash.tabs[tab_idx].items[item_idx] + items.append((tab_idx, item)) + + # TODO: create backups + + for tab_idx, item in items: + stash.tabs[tab_idx].remove(item) + tmp_path.write_bytes(stash.raw()) + + db = get_db() + for _, item in items: + item.write_to_db(db=db) + + tmp_path.replace(stash_path) + + return redirect(f"/stash/{stash_name}", code=303) diff --git a/d2warehouse/app/static/style.css b/d2warehouse/app/static/style.css new file mode 100644 index 0000000..619c2ad --- /dev/null +++ b/d2warehouse/app/static/style.css @@ -0,0 +1,61 @@ +body { + background-color: #000; + font-size: large; + font-family: sans-serif; + color: rgb(240, 240, 240); +} + +.item .name { + font-weight: bold; +} + +.item { + background-color: #444; +} + +.color-rare { + color: rgb(255, 255, 100); +} + +.color-unique { + color: rgb(199, 179, 119); +} + +.color-set { + color: rgb(0, 252, 0); +} + +.color-runeword { + color: rgb(199, 179, 119); +} + + + "FontColorWhite": { "r": 240, "g": 240, "b": 240, "a": 255 }, + "FontColorVeryLightGray": { "r": 240, "g": 240, "b": 240, "a": 255 }, + "FontColorBlack": { "r": 0, "g": 0, "b": 0, "a": 255 }, + "FontColorRed": { "r": 252, "g": 70, "b": 70, "a": 255 }, + "FontColorGreen": { "r": 0, "g": 252, "b": 0, "a": 255 }, + "FontColorBlue": { "r": 110, "g": 110, "b": 255, "a": 255 }, + "FontColorLightGold": { "r": 255, "g": 246, "b": 227, "a": 255 }, // usage: button text, setting text labels + "FontColorGoldYellow" : { "r": 199, "g": 179, "b": 119, "a": 255 }, //Usage example: panel and option subtitles + "FontColorCurrencyGold" : {"r": 209, "g": 195, "b": 120, "a": 255}, // Usage Example: Gold text + "FontColorGold": "$FontColorGoldYellow", + "FontColorDarkGold": { "r": 120, "g": 98, "b": 47, "a": 255 }, + "FontColorBeige" : { "r": 204, "g": 195, "b": 176, "a": 255 }, + "FontColorGray": { "r": 99, "g": 99, "b": 99, "a": 255 }, + "FontColorGrey": "$FontColorGray", + "FontColorOrange": { "r": 255, "g": 168, "b": 0, "a": 255 }, + "FontColorDarkGreen": { "r": 0, "g": 128, "b": 0, "a": 255 }, + "FontColorYellow": { "r": 255, "g": 255, "b": 100, "a": 255 }, + "FontColorLightPurple": { "r": 192, "g": 128, "b": 242, "a": 255 }, + "FontColorLightTeal": { "r": 124, "g": 221, "b": 204, "a": 255 }, + "FontColorLightRed": { "r": 255, "g": 148, "b": 148, "a": 255 }, + "FontColorLightYellow": { "r": 255, "g": 235, "b": 164, "a": 255 }, + "FontColorLightBlue": { "r": 175, "g": 183, "b": 255, "a": 255 }, + "FontColorLightGray": { "r": 148, "g": 148, "b": 148, "a": 255 }, + "FontColorDarkGrayBlue": { "r": 125, "g": 141, "b": 144, "a": 255 }, + "FontColorDarkGrayGold" : { "r": 173, "g": 168, "b": 148, "a": 255 }, // Usage example: attribute points to spend + "FontColorTransparent": { "r": 0, "g": 0, "b": 0, "a": 0 }, + "FontColorPartyOrange": { "r": 196, "g": 129, "b": 0, "a": 255 }, + "FontColorPartyGreen": {"r": 79, "g": 194, "b": 56, "a": 255 }, + diff --git a/d2warehouse/app/templates/item.html b/d2warehouse/app/templates/item.html new file mode 100644 index 0000000..20b45c9 --- /dev/null +++ b/d2warehouse/app/templates/item.html @@ -0,0 +1,14 @@ +
+ ({{tabloop.index0}}, {{itemloop.index0}}) +
    +
  • {{item.name}}
  • + {% if item.quality and item.quality >= 5 %} +
  • {{item.basename}}
  • + {% endif %} + {% if item.stats %} + {% for stat in item.stats %} +
  • {{stat}}
  • + {% endfor %} + {% endif %} +
+
diff --git a/d2warehouse/app/templates/list_stash.html b/d2warehouse/app/templates/list_stash.html new file mode 100644 index 0000000..5ef8f1c --- /dev/null +++ b/d2warehouse/app/templates/list_stash.html @@ -0,0 +1,23 @@ + + + + + Shared Stash + + + +
+ {% for tab in stash.tabs %} +
+ {% set tabloop = loop %} +

Tab {{tabloop.index}}

+ {% for item in tab.items %} + {% set itemloop = loop %} + {% include "item.html" %} + {% endfor %} +
+ {% endfor %} + +
+ + diff --git a/pyproject.toml b/pyproject.toml index 87a12ad..cda8364 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ requires-python = ">=3.10" license = {text = "GPLv3 License"} dependencies = [ "bitarray", + "flask", ] dynamic = ["version"]