mirror of
https://github.com/asphyxia-core/plugins.git
synced 2026-03-21 17:34:46 -05:00
EG Final
This commit is contained in:
parent
fd9866d483
commit
73b27b4e0d
|
|
@ -1,10 +1,14 @@
|
|||
# SOUND VOLTEX
|
||||
|
||||
Plugin Version: **v6.1.2**
|
||||
Plugin Version: **v6.2.0**
|
||||
|
||||
## Provide out of box usable exprience, everything is unlocked and good to go.
|
||||
|
||||
Prerequisite CORE version: v1.50c or above
|
||||
# For full expreience on webui, make sure to copy music database to root of sdvx plugin folder.
|
||||
# Other assets like chat stamps, nemsys avatars, submonitor backgrounds should also be copied to webui/asset folder respectively.
|
||||
# Currently EG Final data for the webui is not fully updated, you might need to update the entry manually if you want customizes.
|
||||
|
||||
Prerequisite CORE version: v1.50d or above
|
||||
|
||||
Supported Versions:
|
||||
|
||||
|
|
@ -33,6 +37,10 @@ Remember to import asset from the game files first time when using webui.
|
|||
Change Log
|
||||
===========
|
||||
|
||||
## 6.2.0
|
||||
|
||||
1. Support EG Final 2025120900.
|
||||
|
||||
## 6.1.2
|
||||
|
||||
1. Hotfix for 神 skill analyzer not showing after passed.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
4541
sdvx@asphyxia/data/exg_gene.ts
Normal file
4541
sdvx@asphyxia/data/exg_gene.ts
Normal file
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,7 @@
|
|||
import { EVENT6, COURSES6, EXTENDS6, VALGENE6 } from '../data/exg';
|
||||
import {getVersion, getRandomIntInclusive} from '../utils';
|
||||
import fs from 'fs';
|
||||
import { get } from 'http';
|
||||
import { EXG } from '../data/exg';
|
||||
import {getVersion, getRandomIntInclusive, getCurrentWeekOfYear, SeededRandom, getWeekStartAndEnd} from '../utils';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export const informationString =
|
||||
`[sz:120] [olc:555555][ol:4][c:ff3333,3333ff,77ff77]Asphyxia
|
||||
|
|
@ -8,7 +9,7 @@ export const informationString =
|
|||
[sz:30][sz:30][c:ffffff,888888]
|
||||
|
||||
[c:00d5ff,888888]ASPHYXIA CORE ${CORE_VERSION}
|
||||
[c:e5f3ff,a3d5ff]SDVX Plugin ver 6.1.0
|
||||
[c:e5f3ff,a3d5ff]SDVX Plugin ver 6.2.0
|
||||
|
||||
|
||||
[f:0][c:ff3333,ffffff]FREE SOFTWARE. BEWARE OF SCAMMERS.
|
||||
|
|
@ -33,17 +34,32 @@ export const common: EPR = async (info, data, send) => {
|
|||
let courses = [];
|
||||
let extend = [];
|
||||
console.log("Calling common function");
|
||||
let exg_data_json = JSON.parse(fs.readFileSync('./plugins/sdvx@asphyxia/data/exg_data.json', 'utf8'));
|
||||
|
||||
events = EVENT6;
|
||||
courses = COURSES6;
|
||||
// EXTENDS6.forEach(val => extend.push(Object.assign({}, val)));
|
||||
extend = EXTENDS6;
|
||||
extend = extend.concat(exg_data_json.extends_data);
|
||||
// extend = extend.concat(exg_data.extends_data);
|
||||
let versionCommonObject: any = {
|
||||
EVENT: [],
|
||||
COURSE: [],
|
||||
EXTEND: [],
|
||||
VALGENE: {},
|
||||
};
|
||||
|
||||
switch(info.method){
|
||||
case 'sv6_common': {
|
||||
versionCommonObject = EXG;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
events = events.concat(versionCommonObject.EVENT);
|
||||
courses = courses.concat(versionCommonObject.COURSE);
|
||||
extend = extend.concat(versionCommonObject.EXTEND);
|
||||
|
||||
let songs = [];
|
||||
|
||||
if (U.GetConfig('enblae_VSync')) {
|
||||
console.log("Enabling VSync");
|
||||
events.push('SUBMONITOR_VSYNC_ENABLE');
|
||||
}
|
||||
|
||||
if (U.GetConfig('unlock_all_songs')) {
|
||||
console.log("Unlocking songs");
|
||||
let songNum = U.GetConfig('music_count');
|
||||
|
|
@ -56,6 +72,14 @@ export const common: EPR = async (info, data, send) => {
|
|||
});
|
||||
|
||||
}
|
||||
|
||||
if(i == 636){ // Everlasting Message ULT
|
||||
songs.push({
|
||||
music_id: K.ITEM('s32', i),
|
||||
music_type: K.ITEM('u8', 5),
|
||||
limited: K.ITEM('u8', 3),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -144,7 +168,7 @@ export const common: EPR = async (info, data, send) => {
|
|||
}
|
||||
}
|
||||
|
||||
if(Math.abs(getVersion(info)) == 6){
|
||||
if(getVersion(info) == 6){
|
||||
extend.push({
|
||||
id: 3,
|
||||
type: 1,
|
||||
|
|
@ -182,6 +206,17 @@ export const common: EPR = async (info, data, send) => {
|
|||
time.setDate(tempDate);
|
||||
const newTime = time.getTime();
|
||||
|
||||
console.log(getCurrentWeekOfYear());
|
||||
|
||||
const seed = parseInt(`${new Date().getFullYear()}${getCurrentWeekOfYear()}`);
|
||||
|
||||
const rng = new SeededRandom(seed);
|
||||
// const weekly_music_id = rng.next() % U.GetConfig('music_count');
|
||||
const weekly_music_id = 636
|
||||
|
||||
console.log(getWeekStartAndEnd().endOfWeek);
|
||||
console.log(getWeekStartAndEnd().startOfWeek);
|
||||
|
||||
console.log("Sending common objects");
|
||||
send.object(
|
||||
{
|
||||
|
|
@ -257,10 +292,10 @@ export const common: EPR = async (info, data, send) => {
|
|||
season: K.ITEM('s32',3),
|
||||
rule: K.ITEM('s32',0),
|
||||
rank_match_target: K.ARRAY('s32', [
|
||||
2,2,2,2,
|
||||
2,2,2,2,
|
||||
1,1,1,1,
|
||||
1,1,1,1,
|
||||
0,1,2,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
0,0,0,0,
|
||||
|
|
@ -274,18 +309,24 @@ export const common: EPR = async (info, data, send) => {
|
|||
is_shop: K.ITEM('bool',true)
|
||||
},
|
||||
valgene: {
|
||||
info: unlock_codes.map(v => ({
|
||||
valgene_name: K.ITEM('str', 'VALKYRIE GENERATOR VOL.' + v),
|
||||
valgene_name_english: K.ITEM('str', 'VALKYRIE GENERATOR VOL.' + v),
|
||||
valgene_id: K.ITEM('s32', v),
|
||||
info: versionCommonObject.VALGENE.info.map(v => ({
|
||||
valgene_name: K.ITEM('str', v.valgene_name),
|
||||
valgene_name_english: K.ITEM('str', v.valgene_name_english),
|
||||
valgene_id: K.ITEM('s32', v.valgene_id),
|
||||
})),
|
||||
catalog: VALGENE6.catalog.map(c => ({
|
||||
catalog: versionCommonObject.VALGENE.catalog.map(c => ({
|
||||
valgene_id: K.ITEM('s32', c.valgene_id),
|
||||
rarity: K.ITEM('s32', c.rarity),
|
||||
item_type: K.ITEM('s32', c.item_type),
|
||||
item_id: K.ITEM('s32', c.item_id),
|
||||
})),
|
||||
},
|
||||
weekly_music: {
|
||||
week_id: K.ITEM('s32',0),
|
||||
music_id: K.ITEM('s32', weekly_music_id),
|
||||
time_start: K.ITEM('u64',BigInt(getWeekStartAndEnd().startOfWeek)),
|
||||
time_end: K.ITEM('u64',BigInt(getWeekStartAndEnd().endOfWeek-999)),
|
||||
},
|
||||
invest:{
|
||||
limit_date: K.ITEM('u64',BigInt(newTime)),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,19 +7,26 @@ import { MatchingRoom } from '../models/matching';
|
|||
let Tracker:MatchingRoom[] = [];
|
||||
|
||||
export const hiscore: EPR = async (info, data, send) => {
|
||||
|
||||
const records = await DB.Find<MusicRecord>(null, { collection: 'music' });
|
||||
|
||||
const version = getVersion(info);
|
||||
|
||||
let limit = $(data).number('limit');
|
||||
let offset = $(data).number('offset');
|
||||
|
||||
const profiles = _.groupBy(
|
||||
await DB.Find<Profile>(null, { collection: 'profile' }),
|
||||
'__refid'
|
||||
);
|
||||
console.log(`Sending hiscore limit:${limit} offset:${offset}`);
|
||||
|
||||
let filtered_records = records.filter(r => r.mid > 0+offset && r.mid <= 0+offset+limit);
|
||||
|
||||
return send.object({
|
||||
sc: {
|
||||
d: _.map(
|
||||
_.groupBy(records, r => {
|
||||
_.groupBy(filtered_records, r => {
|
||||
return `${r.mid}:${r.type}`;
|
||||
}),
|
||||
r => _.maxBy(r, 'score')
|
||||
|
|
@ -39,7 +46,10 @@ export const hiscore: EPR = async (info, data, send) => {
|
|||
lx_nm: K.ITEM('str', profiles[r.__refid][0].name),
|
||||
lx_sc: K.ITEM('u32', r.exscore ?? 0),
|
||||
avg_sc: K.ITEM('u32', r.score),
|
||||
cr: K.ITEM('s32', 8763)
|
||||
avg_ex: K.ITEM('u32', r.exscore ?? 0),
|
||||
cr: K.ITEM('s32', 8763),
|
||||
avg_sc_lv: K.ARRAY('u32', Array.from({length:13}).map(x => r.score)), //Array.from({length:12}).map(x=> r.score)
|
||||
avg_ex_lv: K.ARRAY('u32', Array.from({length:13}).map(x => r.exscore))
|
||||
})),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ function unlock_all_valkgen(items: Partial<Item>[]) {
|
|||
|
||||
export const loadScore: EPR = async (info, data, send) => {
|
||||
console.log("Now loading score");
|
||||
const version = Math.abs(getVersion(info));
|
||||
const version = getVersion(info);
|
||||
console.log("Got version:" + version);
|
||||
let refid = $(data).str('refid', $(data).attr().dataid);
|
||||
if (version === 2) refid = $(data).str('dataid', '0');
|
||||
|
|
@ -52,11 +52,10 @@ export const loadScore: EPR = async (info, data, send) => {
|
|||
console.log('Finding record');
|
||||
const records = await DB.Find<MusicRecord>(refid, { collection: 'music' });
|
||||
|
||||
|
||||
return send.object({
|
||||
music: {
|
||||
info: records.map(r => ({
|
||||
param: K.ARRAY('u32', [
|
||||
info: records.map(r => {
|
||||
let tempArr = [
|
||||
r.mid,
|
||||
r.type,
|
||||
r.score,
|
||||
|
|
@ -68,7 +67,7 @@ export const loadScore: EPR = async (info, data, send) => {
|
|||
r.buttonRate,
|
||||
r.longRate,
|
||||
r.volRate,
|
||||
0,
|
||||
r.volforce ? r.volforce : 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
|
@ -79,8 +78,12 @@ export const loadScore: EPR = async (info, data, send) => {
|
|||
0,
|
||||
0,
|
||||
0,
|
||||
]),
|
||||
})),
|
||||
];
|
||||
|
||||
return {
|
||||
param: K.ARRAY('u32', tempArr),
|
||||
}
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -111,6 +114,7 @@ export const saveScore: EPR = async (info, data, send) => {
|
|||
buttonRate: 0,
|
||||
longRate: 0,
|
||||
volRate: 0,
|
||||
volforce: 0,
|
||||
};
|
||||
|
||||
const score = i.number('score', 0);
|
||||
|
|
@ -125,7 +129,23 @@ export const saveScore: EPR = async (info, data, send) => {
|
|||
record.exscore = exscore;
|
||||
}
|
||||
|
||||
record.clear = Math.max(i.number('clear_type', 0), record.clear);
|
||||
const volforce = i.number('volforce', 0);
|
||||
|
||||
if (isNaN(record.volforce) || record.volforce === null) {
|
||||
console.log("Old Volforce is NaN or null, setting to 0");
|
||||
record.volforce = 0;
|
||||
}
|
||||
|
||||
if (volforce > record.volforce) {
|
||||
record.volforce = volforce;
|
||||
}
|
||||
|
||||
if(i.number('clear_type', 0) == 6 && record.clear >= 4){
|
||||
console.log("Detected Maxxive Clear, but originally UC or PUC, no override.")
|
||||
}else{
|
||||
record.clear = Math.max(i.number('clear_type', 0), record.clear);
|
||||
}
|
||||
|
||||
record.grade = Math.max(i.number('score_grade', 0), record.grade);
|
||||
|
||||
|
||||
|
|
@ -201,12 +221,20 @@ export const save: EPR = async (info, data, send) => {
|
|||
effCLeft: $(data).number('eff_c_left'),
|
||||
effCRight: $(data).number('eff_c_right'),
|
||||
narrowDown: $(data).number('narrow_down'),
|
||||
|
||||
vGateOverRadar: $(data).element('variant_gate').numbers("over_radar")
|
||||
},
|
||||
$inc: {
|
||||
packets: $(data).number('earned_gamecoin_packet'),
|
||||
blocks: $(data).number('earned_gamecoin_block'),
|
||||
blasterEnergy: $(data).number('earned_blaster_energy'),
|
||||
extrackEnergy: $(data).number('earned_extrack_energy'),
|
||||
vGatePower: $(data).element('variant_gate').number('earned_power'),
|
||||
vGateNotes: $(data).element('variant_gate').element('earned_element').number('notes'),
|
||||
vGatePeak: $(data).element('variant_gate').element('earned_element').number('peak'),
|
||||
vGateTsumami: $(data).element('variant_gate').element('earned_element').number('tsumami'),
|
||||
vGateTricky: $(data).element('variant_gate').element('earned_element').number('tricky'),
|
||||
vGateOnehand: $(data).element('variant_gate').element('earned_element').number('onehand'),
|
||||
vGateHandtrip: $(data).element('variant_gate').element('earned_element').number('handtrip'),
|
||||
},
|
||||
}
|
||||
);
|
||||
|
|
@ -385,6 +413,35 @@ export const load: EPR = async (info, data, send) => {
|
|||
profile.appeal_frame = profile.appeal_frame ? profile.appeal_frame : 0;
|
||||
profile.support_team = profile.support_team ? profile.support_team : 0;
|
||||
|
||||
profile.use_pro_team = profile.use_pro_team ? profile.use_pro_team : false;
|
||||
|
||||
profile.vGatePower = profile.vGatePower ? profile.vGatePower : 0;
|
||||
profile.vGateNotes = profile.vGateNotes ? profile.vGateNotes : 0;
|
||||
profile.vGatePeak = profile.vGatePeak ? profile.vGatePeak : 0;
|
||||
profile.vGateTsumami = profile.vGateTsumami ? profile.vGateTsumami : 0;
|
||||
profile.vGateTricky = profile.vGateTricky ? profile.vGateTricky : 0;
|
||||
profile.vGateOnehand = profile.vGateOnehand ? profile.vGateOnehand : 0;
|
||||
profile.vGateHandtrip = profile.vGateHandtrip ? profile.vGateHandtrip : 0;
|
||||
profile.vGateOverRadar = profile.vGateOverRadar ? profile.vGateOverRadar : [];
|
||||
|
||||
if(!profile.vGatePower){ // Data migration
|
||||
await DB.Update<Profile>(
|
||||
refid,
|
||||
{ collection: 'profile' },
|
||||
{
|
||||
$set: {
|
||||
vGatePower: 0,
|
||||
vGateNotes: 0,
|
||||
vGatePeak: 0,
|
||||
vGateTsumami: 0,
|
||||
vGateTricky: 0,
|
||||
vGateOnehand: 0,
|
||||
vGateHandtrip: 0,
|
||||
vGateOverRadar: [],
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return send.pugFile('templates/load.pug', {
|
||||
courses,
|
||||
|
|
@ -435,6 +492,16 @@ export const create: EPR = async (info, data, send) => {
|
|||
blasterCount: 0,
|
||||
blasterEnergy: 0,
|
||||
extrackEnergy: 0,
|
||||
|
||||
vGatePower: 0,
|
||||
vGateNotes: 0,
|
||||
vGatePeak: 0,
|
||||
vGateTsumami: 0,
|
||||
vGateTricky: 0,
|
||||
vGateOnehand: 0,
|
||||
vGateHandtrip: 0,
|
||||
vGateOverRadar: [],
|
||||
|
||||
bgm: 0,
|
||||
subbg: 0,
|
||||
nemsys: 0,
|
||||
|
|
@ -449,6 +516,7 @@ export const create: EPR = async (info, data, send) => {
|
|||
mainbg: 0,
|
||||
appeal_frame: 0,
|
||||
support_team: 0,
|
||||
use_pro_team: false,
|
||||
|
||||
headphone: 0,
|
||||
musicID: 0,
|
||||
|
|
@ -456,6 +524,7 @@ export const create: EPR = async (info, data, send) => {
|
|||
sortType: 0,
|
||||
expPoint: 0,
|
||||
mUserCnt: 0,
|
||||
|
||||
boothFrame: [0, 0, 0, 0, 0]
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,9 +5,9 @@ import { getVersion, IDToCode, GetCounter } from '../utils';
|
|||
import { Mix } from '../models/mix';
|
||||
import { fstat } from 'fs';
|
||||
import { error } from 'console';
|
||||
import { setMaxIdleHTTPParsers } from 'http';
|
||||
import { unpackS3P } from '../s3p';
|
||||
import { secureHeapUsed } from 'crypto';
|
||||
import { music_db } from '..';
|
||||
import { zipFolderToFile } from '../utils/zip';
|
||||
|
||||
export const updateProfile = async (data: {
|
||||
refid: string;
|
||||
|
|
@ -28,6 +28,7 @@ export const updateProfile = async (data: {
|
|||
mainbg?: string;
|
||||
appeal_frame?: string;
|
||||
support_team?: string;
|
||||
use_pro_team?: string;
|
||||
}) => {
|
||||
if (data.refid == null) return;
|
||||
|
||||
|
|
@ -121,6 +122,16 @@ export const updateProfile = async (data: {
|
|||
if (!_.isNaN(validMainbg)) update.mainbg = validMainbg;
|
||||
}
|
||||
|
||||
console.log(data.use_pro_team);
|
||||
|
||||
if (data.use_pro_team !== undefined && data.use_pro_team == "on") {
|
||||
const validUseProTeam = true;
|
||||
update.use_pro_team = validUseProTeam;
|
||||
} else {
|
||||
const validUseProTeam = false;
|
||||
update.use_pro_team = validUseProTeam;
|
||||
}
|
||||
|
||||
await DB.Update<Profile>(
|
||||
data.refid,
|
||||
{ collection: 'profile' },
|
||||
|
|
@ -140,10 +151,6 @@ export const updateMix = async (data: {
|
|||
}
|
||||
|
||||
if (data.creator && data.creator.length > 0) {
|
||||
// const validCreator = data.creator
|
||||
// .toUpperCase()
|
||||
// .replace(/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!?#$&*\-\.\ ]/g, '')
|
||||
// .slice(0, 8);
|
||||
if (data.creator.length > 0) update.creator = data.creator;
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +244,6 @@ export const import_assets = async (data: { path: string }, send: WebUISend) =>
|
|||
|
||||
// await init(wasmUrl);
|
||||
// let ffmpeg = await Wasmer.fromRegistry("wasmer/ffmpeg");
|
||||
let Admzip = require('../../_shared/lib/adm-zip')
|
||||
let path = data.path
|
||||
console.log(path)
|
||||
let fs = require('fs')
|
||||
|
|
@ -266,17 +272,14 @@ export const import_assets = async (data: { path: string }, send: WebUISend) =>
|
|||
return
|
||||
}
|
||||
|
||||
let zip = new Admzip()
|
||||
await fs.promises.readdir(path + "/data/sound/custom").then((files: any) => {
|
||||
// let file = files[0]
|
||||
console.log(files)
|
||||
const files = await fs.promises.readdir(path + "/data/sound/custom")
|
||||
console.log(files)
|
||||
|
||||
for(let i in files){
|
||||
let file = files[i]
|
||||
if(file.endsWith('.s3p')){
|
||||
fs.mkdirSync('./plugins/sdvx@asphyxia/webui/asset/temp/'+file, { recursive: true });
|
||||
// fs.mkdirSync('./plugins/sdvx@asphyxia/webui/asset/audio/'+file.substring(0, 9), { recursive: true });
|
||||
unpackS3P('./plugins/sdvx@asphyxia/webui/asset/temp/'+file, path + "/data/sound/custom/" + file, {})
|
||||
for (const file of files) {
|
||||
if (file.endsWith('.s3p')) {
|
||||
fs.mkdirSync('./plugins/sdvx@asphyxia/webui/asset/temp/' + file, { recursive: true });
|
||||
// fs.mkdirSync('./plugins/sdvx@asphyxia/webui/asset/audio/'+file.substring(0, 9), { recursive: true });
|
||||
unpackS3P('./plugins/sdvx@asphyxia/webui/asset/temp/' + file, path + "/data/sound/custom/" + file, {})
|
||||
// fs.promises.readFileSync('./plugins/sdvx@asphyxia/webui/asset/temp/'+file+'/0.wma').then(async (data: any) => {
|
||||
// const instance = await ffmpeg.entrypoint.run({
|
||||
// args: ["-i", "-", "-f", "wav", "-"],
|
||||
|
|
@ -299,16 +302,13 @@ export const import_assets = async (data: { path: string }, send: WebUISend) =>
|
|||
// console.log(err)
|
||||
// })
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}).finally(() => {
|
||||
|
||||
zip.addLocalFolder('./plugins/sdvx@asphyxia/webui/asset/temp', 'temp')
|
||||
zip.writeZip('./plugins/sdvx@asphyxia/webui/asset/temp.zip')
|
||||
await zipFolderToFile({
|
||||
sourceDir: './plugins/sdvx@asphyxia/webui/asset/temp',
|
||||
outZipPath: './plugins/sdvx@asphyxia/webui/asset/temp.zip',
|
||||
rootInZip: 'temp',
|
||||
})
|
||||
|
||||
await fs.promises.rm('./plugins/sdvx@asphyxia/webui/asset/temp', { recursive: true, force: true }).catch((err: any) => {
|
||||
|
|
@ -465,4 +465,17 @@ export const update_webui_bgm_data = async (data: any, send: WebUISend) => {
|
|||
|
||||
fs.writeFileSync("./plugins/sdvx@asphyxia/webui/asset/json/data.json", JSON.stringify(datajson, null, 2))
|
||||
send.json({status:"ok"})
|
||||
}
|
||||
|
||||
export const update_music_db = async (data: any, send: WebUISend) => {
|
||||
const fs = require('fs')
|
||||
data = JSON.parse(U.parseXML(data.file, false))
|
||||
|
||||
fs.writeFileSync("./plugins/sdvx@asphyxia/webui/asset/json/music_db.json", JSON.stringify(data, null, 2))
|
||||
}
|
||||
|
||||
|
||||
export const sendMdb = async (data: any, send: WebUISend) => {
|
||||
console.log('Sending music_db to WebUI...')
|
||||
send.json(music_db)
|
||||
}
|
||||
|
|
@ -1,8 +1,5 @@
|
|||
import {common,log} from './handlers/common';
|
||||
import {hiscore, rival, saveMix, loadMix, globalMatch} from './handlers/features';
|
||||
// import {} from './handlers/sv4/';
|
||||
// import {} from './handlers/sv5/';
|
||||
// import {} from './handlers/sv6/';
|
||||
import {
|
||||
updateProfile,
|
||||
updateMix,
|
||||
|
|
@ -13,7 +10,9 @@ import {
|
|||
update_webui_nemsys_data,
|
||||
update_webui_stamp_data,
|
||||
update_webui_subbg_data,
|
||||
update_webui_bgm_data
|
||||
update_webui_bgm_data,
|
||||
update_music_db,
|
||||
sendMdb,
|
||||
// sendImg,
|
||||
// sendImgWithID,
|
||||
// getScore,
|
||||
|
|
@ -31,23 +30,27 @@ import {
|
|||
print,
|
||||
} from './handlers/profiles';
|
||||
|
||||
import { MusicRecord } from './models/music_record';
|
||||
import { TRANSLATION_TABLE } from './utils';
|
||||
|
||||
enum Version{
|
||||
Booth = 'game.',
|
||||
II = 'game_2.',
|
||||
GW = 'game_3.',
|
||||
HH = 'game.sv4_',
|
||||
VW = 'game.sv5_',
|
||||
EG = 'game.sv6_',
|
||||
}
|
||||
import { MusicRecord } from './models/music_record';
|
||||
|
||||
export let music_db;
|
||||
|
||||
function load_music_db(){
|
||||
IO.ReadFile('./webui/asset/json/music_db.json',{encoding:'utf8'}).then(data => {
|
||||
music_db = JSON.parse(data);
|
||||
IO.ReadFile('./music_db.xml').then(data => {
|
||||
let mdb_buffer = U.DecodeString(data, 'shift_jis');
|
||||
music_db = U.parseXML(mdb_buffer, false);
|
||||
console.log('music_db loaded, total: '+music_db.mdb.music.length);
|
||||
|
||||
music_db.mdb.music.forEach((m: any) => {
|
||||
let title_name = m.info.title_name["@content"];
|
||||
|
||||
for(let [key, value] of Object.entries(TRANSLATION_TABLE)){
|
||||
title_name = title_name.replaceAll(key, value);
|
||||
}
|
||||
|
||||
m.info.title_name["@content"] = title_name;
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -56,6 +59,7 @@ export function register() {
|
|||
R.Contributor("LatoWolf");
|
||||
R.GameCode('KFC');
|
||||
|
||||
R.Config('enable_VSync', { type: 'boolean', default: false, name:'Enable VSync'} );
|
||||
R.Config('unlock_all_songs', { type: 'boolean', default: false, name:'Unlock All Songs'});
|
||||
R.Config('unlock_all_navigators', { type: 'boolean', default: false, name:'Unlock All Navigators'} );
|
||||
R.Config('unlock_all_appeal_cards', { type: 'boolean', default: false, name:'Unlock All Appeal Cards'});
|
||||
|
|
@ -63,7 +67,7 @@ export function register() {
|
|||
R.Config('use_asphyxia_gameover',{ type: 'boolean', default: true, name:'Use Asphyxia Gameover', desc:'Enable the Asphyxia gameover message after ending the game.'})
|
||||
R.Config('use_blasterpass',{ type: 'boolean', default: true, name:'Use Blaster Pass', desc:'Enable Blaster Pass for VW and EG'});
|
||||
R.Config('new_year_special',{ type: 'boolean', default: true, name:'Use New Year Special', desc:'Enable New Year Special BGM for login'});
|
||||
R.Config('music_count',{ type: 'integer', default: 2200, name:'Music Count', desc:'The maximum id of music in the game.'});
|
||||
R.Config('music_count',{ type: 'integer', default: 2500, name:'Music Count', desc:'The maximum id of music in the game.'});
|
||||
|
||||
R.WebUIEvent('updateProfile', updateProfile);
|
||||
R.WebUIEvent('updateMix', updateMix);
|
||||
|
|
@ -75,9 +79,12 @@ export function register() {
|
|||
R.WebUIEvent('update_webui_chat_stamp', update_webui_stamp_data);
|
||||
R.WebUIEvent('update_webui_subbg', update_webui_subbg_data);
|
||||
R.WebUIEvent('update_webui_bgm', update_webui_bgm_data);
|
||||
R.WebUIEvent('update_music_db', update_music_db);
|
||||
R.WebUIEvent('getMusicDB', sendMdb);
|
||||
|
||||
const MultiRoute = (method: string, handler: EPR | boolean) => {
|
||||
R.Route(`game.sv6_${method}`, handler);
|
||||
R.Route(`game.sv7_${method}`, handler);
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -140,4 +147,8 @@ export function register() {
|
|||
|
||||
|
||||
R.Unhandled();
|
||||
|
||||
if (IO.Exists('./music_db.xml')) {
|
||||
load_music_db();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,4 +10,5 @@ export interface MusicRecord {
|
|||
buttonRate: number;
|
||||
longRate: number;
|
||||
volRate: number;
|
||||
volforce: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,16 @@ export interface Profile {
|
|||
sortType: number;
|
||||
headphone: number;
|
||||
blasterEnergy: number;
|
||||
|
||||
vGatePower: number;
|
||||
vGateNotes: number;
|
||||
vGatePeak: number;
|
||||
vGateTsumami: number;
|
||||
vGateTricky: number;
|
||||
vGateOnehand: number;
|
||||
vGateHandtrip: number;
|
||||
vGateOverRadar: number[];
|
||||
|
||||
blasterCount: number;
|
||||
extrackEnergy: number;
|
||||
appeal_frame: number;
|
||||
|
|
@ -50,5 +60,7 @@ export interface Profile {
|
|||
stampD_R: number;
|
||||
mainbg: number;
|
||||
|
||||
use_pro_team: boolean;
|
||||
|
||||
boothFrame: number[];
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
export function unpackS3P(directory, filePath, names) {
|
||||
export function unpackS3P(directory: string, filePath: string, names: { [x: string]: string | number; }) {
|
||||
const stream = fs.readFileSync(filePath);
|
||||
if (stream.slice(0, 4).toString() !== 'S3P0') {
|
||||
throw new Error('Invalid S3P file');
|
||||
|
|
@ -33,7 +33,6 @@ export function unpackS3P(directory, filePath, names) {
|
|||
offset += 4;
|
||||
const headerExtra = stream.slice(offset, offset + hlen - 8);
|
||||
offset += hlen - 8;
|
||||
// const [wmaFileLength, , , , , , ,] = new Uint32Array(headerExtra.buffer);
|
||||
|
||||
const data = stream.slice(offset, offset + length - hlen);
|
||||
offset += length - hlen;
|
||||
|
|
@ -44,17 +43,17 @@ export function unpackS3P(directory, filePath, names) {
|
|||
}
|
||||
}
|
||||
|
||||
export function packS3P(directory, output, names) {
|
||||
export function packS3P(directory: string, output: string, names: { [x: string]: string | number; }) {
|
||||
let paths = fs.readdirSync(directory);
|
||||
if (names) {
|
||||
const namesBack = {};
|
||||
for (const key in names) {
|
||||
namesBack[names[key]] = key;
|
||||
}
|
||||
paths = paths.filter((i) => namesBack[i.split('.')[0]]);
|
||||
paths.sort((a, b) => namesBack[a.split('.')[0]] - namesBack[b.split('.')[0]]);
|
||||
paths = paths.filter((i: string) => namesBack[i.split('.')[0]]);
|
||||
paths.sort((a: string, b: string) => namesBack[a.split('.')[0]] - namesBack[b.split('.')[0]]);
|
||||
} else {
|
||||
paths.sort((a, b) => parseInt(a.split('.')[0]) - parseInt(b.split('.')[0]));
|
||||
paths.sort((a: string, b: string) => parseInt(a.split('.')[0]) - parseInt(b.split('.')[0]));
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
|
|
@ -119,7 +118,7 @@ function usage() {
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
function loadNames(filePath) {
|
||||
function loadNames(filePath: string) {
|
||||
const base = path.join(path.dirname(filePath), path.basename(filePath, path.extname(filePath)));
|
||||
const filenames = {};
|
||||
|
||||
|
|
@ -149,63 +148,63 @@ function loadNames(filePath) {
|
|||
return filenames;
|
||||
}
|
||||
|
||||
// function main() {
|
||||
// if (process.argv.length !== 5 && process.argv.length !== 6) {
|
||||
// usage();
|
||||
// }
|
||||
// if (process.argv[2] !== 'pack' && process.argv[2] !== 'unpack') {
|
||||
// usage();
|
||||
// }
|
||||
function main() {
|
||||
if (process.argv.length !== 5 && process.argv.length !== 6) {
|
||||
usage();
|
||||
}
|
||||
if (process.argv[2] !== 'pack' && process.argv[2] !== 'unpack') {
|
||||
usage();
|
||||
}
|
||||
|
||||
// const s3p = process.argv[3];
|
||||
// const directory = process.argv[4];
|
||||
const s3p = process.argv[3];
|
||||
const directory = process.argv[4];
|
||||
|
||||
// let names = {};
|
||||
// if (process.argv.length === 6) {
|
||||
// names = loadNames(process.argv[5]);
|
||||
// } else {
|
||||
// names = loadNames(s3p);
|
||||
// }
|
||||
let names = {};
|
||||
if (process.argv.length === 6) {
|
||||
names = loadNames(process.argv[5]);
|
||||
} else {
|
||||
names = loadNames(s3p);
|
||||
}
|
||||
|
||||
// if (!names) {
|
||||
// console.log('W: Filenames not loaded');
|
||||
// }
|
||||
if (!names) {
|
||||
console.log('W: Filenames not loaded');
|
||||
}
|
||||
|
||||
// if (process.argv[2] === 'pack') {
|
||||
// if (!fs.existsSync(directory)) {
|
||||
// console.error(`F: No such file or directory ${directory}`);
|
||||
// process.exit(1);
|
||||
// }
|
||||
if (process.argv[2] === 'pack') {
|
||||
if (!fs.existsSync(directory)) {
|
||||
console.error(`F: No such file or directory ${directory}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// const files = fs.readdirSync(directory);
|
||||
// if (!files.every((i) => /^\d+\.wma$/.test(i) || Object.values(names).includes(i.split('.')[0]))) {
|
||||
// console.error('F: Files must all be [number].wma');
|
||||
// process.exit(1);
|
||||
// }
|
||||
const files = fs.readdirSync(directory);
|
||||
if (!files.every((i: string) => /^\d+\.wma$/.test(i) || Object.values(names).includes(i.split('.')[0]))) {
|
||||
console.error('F: Files must all be [number].wma');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// const dirname = path.dirname(s3p);
|
||||
// if (dirname) {
|
||||
// fs.mkdirSync(dirname, { recursive: true });
|
||||
// }
|
||||
const dirname = path.dirname(s3p);
|
||||
if (dirname) {
|
||||
fs.mkdirSync(dirname, { recursive: true });
|
||||
}
|
||||
|
||||
// packS3P(directory, s3p, names);
|
||||
// console.log(`I: ${s3p}`);
|
||||
// } else {
|
||||
// if (!fs.existsSync(s3p)) {
|
||||
// console.error(`F: No such file or directory ${s3p}`);
|
||||
// process.exit(1);
|
||||
// }
|
||||
packS3P(directory, s3p, names);
|
||||
console.log(`I: ${s3p}`);
|
||||
} else {
|
||||
if (!fs.existsSync(s3p)) {
|
||||
console.error(`F: No such file or directory ${s3p}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory()) {
|
||||
// console.error('F: Output is not a directory');
|
||||
// process.exit(1);
|
||||
// }
|
||||
if (fs.existsSync(directory) && !fs.lstatSync(directory).isDirectory()) {
|
||||
console.error('F: Output is not a directory');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// fs.mkdirSync(directory, { recursive: true });
|
||||
// unpackS3P(directory, s3p, names);
|
||||
// }
|
||||
// }
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
unpackS3P(directory, s3p, names);
|
||||
}
|
||||
}
|
||||
|
||||
// if (require.main === module) {
|
||||
// main();
|
||||
// }
|
||||
if (require.main === module) {
|
||||
main();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,7 +13,20 @@ game
|
|||
headphone(__type="u8") #{headphone}
|
||||
blaster_energy(__type="u32") #{blasterEnergy}
|
||||
blaster_count(__type="u32") 0
|
||||
extrack_energy(__type="u16") #{extrackEnergy}
|
||||
//- extrack_energy(__type="u16") #{extrackEnergy}
|
||||
variant_gate
|
||||
power(__type="s32") #{vGatePower}
|
||||
element
|
||||
notes(__type="s32") #{vGateNotes}
|
||||
peak(__type="s32") #{vGatePeak}
|
||||
tsumami(__type="s32") #{vGateTsumami}
|
||||
tricky(__type="s32") #{vGateTricky}
|
||||
onehand(__type="s32") #{vGateOnehand}
|
||||
handtrip(__type="s32") #{vGateHandtrip}
|
||||
if vGateOverRadar.length > 0
|
||||
over_radar(__type="s32" __count=vGateOverRadar.length) #{vGateOverRadar.join(" ")}
|
||||
else
|
||||
over_radar(__type="s32" __count="0")/
|
||||
|
||||
hispeed(__type="s32") #{hiSpeed}
|
||||
lanespeed(__type="u32") #{laneSpeed}
|
||||
|
|
@ -112,10 +125,12 @@ game
|
|||
ultimate_rate(__type="s32") 0
|
||||
rank_play_cnt(__type="s32") 1
|
||||
ultimate_play_cnt(__type="s32") 1
|
||||
|
||||
//- additional_info
|
||||
|
||||
support_team_id(__type="s32") #{support_team}
|
||||
|
||||
if use_pro_team
|
||||
additional_info
|
||||
pro_team_id(val= support_team)
|
||||
else
|
||||
support_team_id(__type="s32") #{support_team}
|
||||
|
||||
if mixes
|
||||
each mix in mixes
|
||||
|
|
@ -130,4 +145,9 @@ game
|
|||
jacket_id(__type="s32") #{mix.jacket}
|
||||
tag_bit(__type="s32") #{mix.tag}
|
||||
like_flg(__type="u8") 0
|
||||
|
||||
//- weekly_music
|
||||
//- week_id(__type="s32") 0
|
||||
//- music_id(__type="s32") 1660
|
||||
//- music_type(__type="s32") 5
|
||||
//- exscore(__type="u32") 3000
|
||||
//- rank(__type="s32") 5
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import {Counter} from './models/counter';
|
||||
import { music_db } from '.';
|
||||
|
||||
// import { music_db } from '.';
|
||||
|
||||
export function IDToCode(id: number) {
|
||||
const padded = _.padStart(id.toString(), 8);
|
||||
|
|
@ -23,6 +24,7 @@ export function getVersion(info: EamuseInfo) {
|
|||
if (info.method.startsWith('sv4')) return 4;
|
||||
if (info.method.startsWith('sv5')) return 5;
|
||||
if (info.method.startsWith('sv6')) return 6;
|
||||
if (info.method.startsWith('sv7')) return 7;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -159,4 +161,109 @@ export function send_webhook(data: any) {
|
|||
});
|
||||
req.write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentWeekOfYear(date = new Date()) {
|
||||
// Clone the date to avoid modifying the original
|
||||
const current = date.getTime();
|
||||
|
||||
// Set the first day of the year
|
||||
const startOfYear = new Date(date.getFullYear(), 0, 1).getTime();
|
||||
|
||||
// Calculate the day of the year
|
||||
const dayOfYear = ((current - startOfYear + 1) / 86400000);
|
||||
|
||||
// ISO 8601 weeks start on Monday and the first week of the year must contain Jan 4th.
|
||||
// Adjust the date to the nearest Thursday (ISO 8601 rule).
|
||||
const adjustedDate = new Date(
|
||||
date.getFullYear(),
|
||||
date.getMonth(),
|
||||
date.getDate() + (4 - (date.getDay() || 7))
|
||||
);
|
||||
|
||||
const startOfISOYear = new Date(adjustedDate.getFullYear(), 0, 1);
|
||||
const firstWeekDay = startOfISOYear.getDay() || 7;
|
||||
|
||||
// Calculate ISO week number
|
||||
return Math.ceil((adjustedDate.getTime() - startOfISOYear.getTime() + (firstWeekDay - 1) * 86400000) / (7 * 86400000));
|
||||
}
|
||||
|
||||
export function getWeekStartAndEnd(date = new Date()) {
|
||||
// Clone the input date to avoid modifying the original
|
||||
const current = new Date(date.getTime());
|
||||
|
||||
// Get the day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday)
|
||||
const dayOfWeek = current.getDay();
|
||||
|
||||
// Adjust to the start of the week (Monday)
|
||||
const diffToMonday = (dayOfWeek === 0 ? -6 : 1) - dayOfWeek; // Monday = 1, Sunday = 0
|
||||
const startOfWeek = new Date(current.setDate(current.getDate() + diffToMonday));
|
||||
startOfWeek.setHours(0, 0, 0, 0); // Set time to midnight
|
||||
|
||||
// Clone the startOfWeek and add 6 days to get the end of the week
|
||||
const endOfWeek = new Date(startOfWeek.getTime());
|
||||
endOfWeek.setDate(endOfWeek.getDate() + 6);
|
||||
endOfWeek.setHours(23, 59, 59, 999); // Set time to end of day
|
||||
|
||||
return {
|
||||
startOfWeek: startOfWeek.getTime(), // Timestamp for the start of the week
|
||||
endOfWeek: endOfWeek.getTime(), // Timestamp for the end of the week
|
||||
};
|
||||
}
|
||||
|
||||
export class SeededRandom {
|
||||
|
||||
seed: number;
|
||||
|
||||
constructor(seed) {
|
||||
this.seed = seed % 2147483647; // A prime number
|
||||
if (this.seed <= 0) this.seed += 2147483646; // Avoid zero seed
|
||||
}
|
||||
|
||||
next() {
|
||||
this.seed = (this.seed * 16807) % 2147483647; // LCG formula
|
||||
return this.seed;
|
||||
}
|
||||
|
||||
nextFloat() {
|
||||
return (this.next() - 1) / 2147483646; // Convert to [0, 1)
|
||||
}
|
||||
}
|
||||
|
||||
export const TRANSLATION_TABLE = {
|
||||
"龕": "€",
|
||||
"釁": "🍄",
|
||||
"驩": "Ø",
|
||||
"曦": "à",
|
||||
"齷": "é",
|
||||
"骭": "ü",
|
||||
"齶": "♡",
|
||||
"彜": "ū",
|
||||
"罇": "ê",
|
||||
"雋": "Ǜ",
|
||||
"鬻": "♃",
|
||||
"鬥": "Ã",
|
||||
"鬆": "Ý",
|
||||
"曩": "è",
|
||||
"驫": "ā",
|
||||
"齲": "♥",
|
||||
"騫": "á",
|
||||
"趁": "Ǣ",
|
||||
"鬮": "¡",
|
||||
"盥": "⚙︎",
|
||||
"隍": "︎Ü",
|
||||
"頽": "ä",
|
||||
"餮": "Ƶ",
|
||||
"黻": "*",
|
||||
"蔕": "ũ",
|
||||
"闃": "Ā",
|
||||
"饌": "²",
|
||||
"煢": "ø",
|
||||
"鑷": "ゔ",
|
||||
"=墸Σ": "=͟͟͞ Σ",
|
||||
"鹹": "Ĥ",
|
||||
"瀑i": "Ài",
|
||||
"疉": "Ö",
|
||||
"鑒": "₩",
|
||||
"Ryu??": "Ryu☆",
|
||||
}
|
||||
184
sdvx@asphyxia/utils/zip.ts
Normal file
184
sdvx@asphyxia/utils/zip.ts
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
function crc32(buffer: Buffer): number {
|
||||
let crc = 0xffffffff;
|
||||
|
||||
for (let i = 0; i < buffer.length; i++) {
|
||||
crc ^= buffer[i];
|
||||
for (let j = 0; j < 8; j++) {
|
||||
const mask = -(crc & 1);
|
||||
crc = (crc >>> 1) ^ (0xedb88320 & mask);
|
||||
}
|
||||
}
|
||||
|
||||
return (crc ^ 0xffffffff) >>> 0;
|
||||
}
|
||||
|
||||
function toDosTimeDate(date: Date): { time: number; date: number } {
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
const hours = date.getHours();
|
||||
const minutes = date.getMinutes();
|
||||
const seconds = Math.floor(date.getSeconds() / 2);
|
||||
|
||||
const dosTime = (hours << 11) | (minutes << 5) | seconds;
|
||||
const dosDate = ((Math.max(year, 1980) - 1980) << 9) | (month << 5) | day;
|
||||
|
||||
return { time: dosTime & 0xffff, date: dosDate & 0xffff };
|
||||
}
|
||||
|
||||
async function listFilesRecursive(rootDir: string): Promise<string[]> {
|
||||
const results: string[] = [];
|
||||
|
||||
async function walk(currentDir: string) {
|
||||
const entries = await fs.promises.readdir(currentDir, { withFileTypes: true });
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(currentDir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
await walk(fullPath);
|
||||
} else if (entry.isFile()) {
|
||||
results.push(fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await walk(rootDir);
|
||||
return results;
|
||||
}
|
||||
|
||||
function normalizeZipPath(p: string): string {
|
||||
return p.replace(/\\/g, '/');
|
||||
}
|
||||
|
||||
export async function zipFolderToFile(params: {
|
||||
sourceDir: string;
|
||||
outZipPath: string;
|
||||
rootInZip?: string;
|
||||
}): Promise<void> {
|
||||
const sourceDir = path.resolve(params.sourceDir);
|
||||
const outZipPath = path.resolve(params.outZipPath);
|
||||
const rootInZip = params.rootInZip ? normalizeZipPath(params.rootInZip).replace(/^\/+|\/+$/g, '') : '';
|
||||
|
||||
await fs.promises.mkdir(path.dirname(outZipPath), { recursive: true });
|
||||
|
||||
const files = await listFilesRecursive(sourceDir);
|
||||
|
||||
const out = fs.createWriteStream(outZipPath);
|
||||
let offset = 0;
|
||||
|
||||
type CentralEntry = {
|
||||
fileName: string;
|
||||
crc: number;
|
||||
compressedSize: number;
|
||||
uncompressedSize: number;
|
||||
modTime: number;
|
||||
modDate: number;
|
||||
localHeaderOffset: number;
|
||||
};
|
||||
|
||||
const central: CentralEntry[] = [];
|
||||
|
||||
const writeBuffer = async (buf: Buffer) => {
|
||||
if (buf.length === 0) return;
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
out.write(buf, (err) => (err ? reject(err) : resolve()));
|
||||
});
|
||||
offset += buf.length;
|
||||
};
|
||||
|
||||
for (const filePath of files) {
|
||||
const stat = await fs.promises.stat(filePath);
|
||||
const data = await fs.promises.readFile(filePath);
|
||||
|
||||
const rel = normalizeZipPath(path.relative(sourceDir, filePath));
|
||||
if (!rel || rel.startsWith('..') || path.isAbsolute(rel)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const fileName = rootInZip ? `${rootInZip}/${rel}` : rel;
|
||||
const fileNameBytes = Buffer.from(fileName, 'utf8');
|
||||
|
||||
const crc = crc32(data);
|
||||
const uncompressedSize = data.length;
|
||||
const compressedSize = data.length;
|
||||
const { time: modTime, date: modDate } = toDosTimeDate(stat.mtime);
|
||||
|
||||
const localHeaderOffset = offset;
|
||||
|
||||
const localHeader = Buffer.alloc(30);
|
||||
localHeader.writeUInt32LE(0x04034b50, 0); // Local file header signature
|
||||
localHeader.writeUInt16LE(20, 4); // Version needed to extract
|
||||
localHeader.writeUInt16LE(0, 6); // General purpose bit flag
|
||||
localHeader.writeUInt16LE(0, 8); // Compression method (0 = store)
|
||||
localHeader.writeUInt16LE(modTime, 10);
|
||||
localHeader.writeUInt16LE(modDate, 12);
|
||||
localHeader.writeUInt32LE(crc, 14);
|
||||
localHeader.writeUInt32LE(compressedSize, 18);
|
||||
localHeader.writeUInt32LE(uncompressedSize, 22);
|
||||
localHeader.writeUInt16LE(fileNameBytes.length, 26);
|
||||
localHeader.writeUInt16LE(0, 28); // Extra field length
|
||||
|
||||
await writeBuffer(localHeader);
|
||||
await writeBuffer(fileNameBytes);
|
||||
await writeBuffer(data);
|
||||
|
||||
central.push({
|
||||
fileName,
|
||||
crc,
|
||||
compressedSize,
|
||||
uncompressedSize,
|
||||
modTime,
|
||||
modDate,
|
||||
localHeaderOffset,
|
||||
});
|
||||
}
|
||||
|
||||
const centralDirOffset = offset;
|
||||
|
||||
for (const entry of central) {
|
||||
const fileNameBytes = Buffer.from(entry.fileName, 'utf8');
|
||||
const header = Buffer.alloc(46);
|
||||
|
||||
header.writeUInt32LE(0x02014b50, 0); // Central directory file header signature
|
||||
header.writeUInt16LE(20, 4); // Version made by
|
||||
header.writeUInt16LE(20, 6); // Version needed to extract
|
||||
header.writeUInt16LE(0, 8); // General purpose bit flag
|
||||
header.writeUInt16LE(0, 10); // Compression method
|
||||
header.writeUInt16LE(entry.modTime, 12);
|
||||
header.writeUInt16LE(entry.modDate, 14);
|
||||
header.writeUInt32LE(entry.crc, 16);
|
||||
header.writeUInt32LE(entry.compressedSize, 20);
|
||||
header.writeUInt32LE(entry.uncompressedSize, 24);
|
||||
header.writeUInt16LE(fileNameBytes.length, 28);
|
||||
header.writeUInt16LE(0, 30); // Extra field length
|
||||
header.writeUInt16LE(0, 32); // File comment length
|
||||
header.writeUInt16LE(0, 34); // Disk number start
|
||||
header.writeUInt16LE(0, 36); // Internal file attributes
|
||||
header.writeUInt32LE(0, 38); // External file attributes
|
||||
header.writeUInt32LE(entry.localHeaderOffset, 42);
|
||||
|
||||
await writeBuffer(header);
|
||||
await writeBuffer(fileNameBytes);
|
||||
}
|
||||
|
||||
const centralDirSize = offset - centralDirOffset;
|
||||
|
||||
const eocd = Buffer.alloc(22);
|
||||
eocd.writeUInt32LE(0x06054b50, 0); // End of central directory signature
|
||||
eocd.writeUInt16LE(0, 4); // Number of this disk
|
||||
eocd.writeUInt16LE(0, 6); // Disk where central directory starts
|
||||
eocd.writeUInt16LE(central.length, 8); // Number of central directory records on this disk
|
||||
eocd.writeUInt16LE(central.length, 10); // Total number of central directory records
|
||||
eocd.writeUInt32LE(centralDirSize, 12);
|
||||
eocd.writeUInt32LE(centralDirOffset, 16);
|
||||
eocd.writeUInt16LE(0, 20); // ZIP file comment length
|
||||
|
||||
await writeBuffer(eocd);
|
||||
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
out.end(() => resolve());
|
||||
out.on('error', reject);
|
||||
});
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user