Skip to content

Commit

Permalink
Initial Commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Danny Schumacher committed Jan 6, 2020
0 parents commit 6c368f1
Show file tree
Hide file tree
Showing 42 changed files with 15,148 additions and 0 deletions.
6 changes: 6 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.gitignore
.vscode
README.md
node_modules
dist
out
26 changes: 26 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
PORT=3030

#read 4 bits instead of 3 for stash
EXTENDED_STASH=true

#where are saves
SAVE_DIR=/mnt/d/Git/d2/113c/d2s/save
#SAVE_DIR=/Users/drschuma/Documents/d2/113c/d2s/save

#mpq data. txt files, images, and palette data
MPQ_DATA_DIR=/mnt/d/Git/d2/113c/data
#MPQ_DATA_DIR=/Users/drschuma/Documents/d2/113c/data

#optional img directory if different than mpq
#IMG_DATA_DIR=/Users/drschuma/Documents/d2/113c/data

VUE_APP_TITLE=D2S-UI

#VUE_APP_INV_WIDTH=10
#VUE_APP_INV_HEIGHT=4

#VUE_APP_STASH_WIDTH=10
#VUE_APP_STASH_HEIGHT=10

#VUE_APP_CUBE_WIDTH=5
#VUE_APP_CUBE_HEIGHT=5
18 changes: 18 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module.exports = {
root: true,
env: {
node: true,
},
extends: [
'plugin:vue/essential',
'@vue/airbnb',
],
rules: {
'no-param-reassign': [2, { props: false }],
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
},
parserOptions: {
parser: 'babel-eslint',
},
};
25 changes: 25 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.DS_Store
node_modules
/dist
/out

build.js

# local env files
.env.local
.env.*.local

# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?


18 changes: 18 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"args": ["${workspaceRoot}/app.ts"],
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"sourceMaps": true,
"cwd": "${workspaceRoot}",
"protocol": "inspector",
}
]
}
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM node:10-alpine

WORKDIR /usr/src/app

RUN apk update && \
apk upgrade && \
apk add --no-cache --virtual install \
python \
make \
g++ \
git

COPY package*.json ./

RUN npm install --unsafe-perm && \
apk del install

COPY . .

RUN npm run build && \
npm prune --production

CMD ["node", "./out/app.js"]
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
##### Build
```
docker build . -t dschu012/d2s:latest
```

Run
```
docker run dschu012/d2s:latest \
-p 8080:3030 \
-e EXTENDED_STASH=true
-v /path/to/saves:/data/saves:ro \
-v /path/to/txt:/data/txt:ro \
-v /path/to/img:/data/img:ro \
```

##### Examples
* https://diablo.dannyschumacher.com/#/
* https://resurgence.dannyschumacher.com/#/
13 changes: 13 additions & 0 deletions api/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import express from "express";
import d2s from "./v1/d2s.routes";
import img from "./v1/img.routes";
import config from "./v1/config.routes";

let router: express.Router = express.Router();

router.use("/v1/d2s", d2s);
router.use("/v1/img", img);
router.use("/v1/config", config);


export default router;
19 changes: 19 additions & 0 deletions api/v1/config.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import express from "express";
import dotenv from "dotenv";

dotenv.config();

let router: express.Router = express.Router();

router.get("/", async (req, res) => {
res.json({
VUE_APP_INV_WIDTH: process.env.VUE_APP_INV_WIDTH || 10,
VUE_APP_INV_HEIGHT: process.env.VUE_APP_INV_HEIGHT || 4,
VUE_APP_STASH_WIDTH: process.env.VUE_APP_STASH_WIDTH || 6,
VUE_APP_STASH_HEIGHT: process.env.VUE_APP_STASH_HEIGHT || 8,
VUE_APP_CUBE_WIDTH: process.env.VUE_APP_CUBE_WIDTH || 3,
VUE_APP_CUBE_HEIGHT: process.env.VUE_APP_CUBE_HEIGHT || 4,
});
});

export default router;
60 changes: 60 additions & 0 deletions api/v1/d2s.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import express from "express";
import path from "path";
import fs from "fs";
import { read, readConstantData, types } from "d2s";
import dotenv from "dotenv";
import glob from "glob";

dotenv.config();

let router: express.Router = express.Router();
let c: types.IConstantData;

/*
* Load up constant data when start server
*/
(async () => {
let base = process.env.MPQ_DATA_DIR as string;
let files = {} as any;
let dir = path.join(`${base}/global/excel/`);
try {
fs.readdirSync(dir).forEach(file => {
if (file.endsWith(".txt")) {
files[file] = fs.readFileSync(path.join(dir, file), 'utf8');
}
});
dir = path.join(`${base}/local/LNG/ENG/`);
fs.readdirSync(dir).forEach(file => {
if (file.endsWith(".txt")) {
files[file] = fs.readFileSync(path.join(dir, file), 'utf8');
}
});
c = await readConstantData(files);
} catch (e) {
console.error(e);
process.exit(1);
}
})();

router.get("/character/:character", async (req, res) => {
try {
const base = process.env.SAVE_DIR as string;
const files = await glob.sync(path.join(base, `**/${req.params.character}?(.d2s)`), { nodir: true, nocase: true });
if(!files.length) {
throw new Error(`${req.params.character} save not found.`);
}
const buffer = fs.readFileSync(files[0]);
const s = await read(buffer, c, { extendedStash: process.env.EXTENDED_STASH == "true"});
res.json(s);
} catch (e) {
console.log(e);
}
});

router.get("/list", async (req, res) => {
const base = process.env.SAVE_DIR as string;
const files = await glob.sync(path.join(base, `**`), { nodir: true }).map(file => path.basename(file, '.d2s'));
res.json(files);
});

export default router;
147 changes: 147 additions & 0 deletions api/v1/img.routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import express from "express";
import path from "path";
import fs from "fs";
import dotenv from "dotenv";
import jimp from "jimp";
import glob from "glob";

dotenv.config();

let router: express.Router = express.Router();

const palette = [] as any;
const mappings = [] as any;

const colormaps = {
1: '/global/items/Palette/grey.dat',
2: '/global/items/Palette/grey2.dat',
5: '/global/items/Palette/greybrown.dat',
6: '/global/items/Palette/invgrey.dat',
7: '/global/items/Palette/invgrey2.dat',
8: '/global/items/Palette/invgreybrown.dat',
};

const colors = {
whit: 0,
lgry: 1,
dgry: 2,
blac: 3,
lblu: 4,
dblu: 5,
cblu: 6,
lred: 7,
dred: 8,
cred: 9,
lgrn: 10,
dgrn: 11,
cgrn: 12,
lyel: 13,
dyel: 14,
lgld: 15,
dgld: 16,
lpur: 17,
dpur: 18,
oran: 19,
bwht: 20,
};


const base = process.env.IMG_DATA_DIR as string || process.env.MPQ_DATA_DIR as string;
/*
* Load up constant data when start server
*/
(async () => {

async function loadPalette(f: string) {
const buffer = fs.readFileSync(path.join(base, f));
for (let i = 0; i < 256; i += 1) {
palette.push([buffer[i * 3 + 2], buffer[i * 3 + 1], buffer[i * 3]]);
}
};
async function loadMapping(key: any, f: string) {
const buffer = fs.readFileSync(path.join(base, f));
const mapping = [];
for (let i = 0; i < Object.keys(colors).length; i += 1) {
mapping.push(buffer.slice(0 + (i * 256), 256 + (i * 256)));
}
mappings[key] = mapping;
};

try {
loadPalette(`/global/palette/ACT1/pal.dat`);
for(const key in colormaps) {
loadMapping(key, colormaps[key]);
}
} catch(e) {
console.log(e);
process.exit(1);
}
})();


router.get("/:file.png", async (req, res) => {
try {
//we only care about first frame
const files = await glob.sync(path.join(base, '/global/items', `${req.params.file}.dc6`), { nodir: true, nocase: true });
if(files.length === 0) {
throw new Error(`File not found ${req.params.file}.dc6`);
}
const dc6 = fs.readFileSync(files[0]);
const width = dc6.readUInt32LE(32);
const height = dc6.readUInt32LE(36);
const length = dc6.readUInt32LE(56);
let x = 0, y = height - 1;
const indexed = [] as any;
for (let i = 0; i < height; i += 1) {
indexed[i] = Array(width).fill(255);
}
for (let i = 0; i < length;) {
let b = dc6.readUInt8(60 + i++);
if (b === 0x80) { //eol
x = 0, y--;
} else if (b & 0x80) {
x += b & 0x7F; //transparent repeat for N bytes
} else {
//read N bytes
for(let j = 0; j < b; j++) {
indexed[y][x++] = dc6.readUInt8(60 + i++);
}
}
}
new jimp(width, height, async function (err, image) {
let data = image.bitmap.data;
for (let y = 0; y < height; y += 1) {
for (let x = 0; x < width; x += 1) {
let paletteIdx = indexed[y][x];
const offset = (y * width + x) * 4;
if(paletteIdx === 255) { //transparent
continue;
}
if(req.query.inv_transform && req.query.transform_color) {
let transformIdx = colors[req.query.transform_color];
if(transformIdx >= 0 && mappings[req.query.inv_transform]) {
paletteIdx = mappings[req.query.inv_transform][transformIdx][paletteIdx];
}
}
const rgb = palette[paletteIdx];
data[offset] = rgb[0];
data[offset + 1] = rgb[1];
data[offset + 2] = rgb[2];
data[offset + 3] = 255;
}
}
let buffer = await image.getBufferAsync(jimp.MIME_PNG);
res.writeHead(200, {
'Content-Type': 'image/png',
'Cache-Control': 'public, max-age=31557600, s-maxage=31557600'
});
res.end(buffer, 'binary');
})
} catch (e) {
console.log(e);
res.writeHead(404);
res.end();
}
});

export default router;
Loading

0 comments on commit 6c368f1

Please sign in to comment.