Skip to content

Commit e016ff1

Browse files
committed
Add readable package preview support
1 parent a3c12c0 commit e016ff1

6 files changed

Lines changed: 125 additions & 12 deletions

File tree

README.ja.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ GalAid はこれらを小さな診断レポートと手順に整理します。
4848
- デスクトップ beta でネイティブフォルダ選択と再帰スキャン
4949
- デスクトップのパッケージ/イメージ事前チェックは ZIP ディレクトリ、同梱またはローカル 7z ツールによる RAR/7z/LZH/LHA/ARJ/CAB/TAR 系メタデータを読み、起動、インストールメディア、ランタイム修復サンプルを個別に強調する。ディスクイメージ種別、`autorun.inf + Start.exe + data1.cab` のような古いインストールディスク構造、同梱ランタイム修復項目も扱い、起動ボタンからローカル展開、ISO マウント/イメージ展開、再スキャン、推奨入口の起動まで進められる。ゲーム本体がなく `setup.exe/autorun.exe/.msi` または `autorun.inf` が指すインストール入口だけの場合は、インストールメディア入口として分けて扱う
5050
- Markdown 診断レポートのコピー/ダウンロード
51-
- 手順、環境チェック、レシピ一致、ファイル一覧、`package-previews.json` のパッケージ/イメージ事前チェック概要をまとめたサポートバンドルの出力
51+
- 手順、環境チェック、レシピ一致、ファイル一覧、`package-previews.md``package-previews.json` のパッケージ/イメージ事前チェック概要をまとめたサポートバンドルの出力
5252

5353
## 診断言語
5454

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ The support bundle is a local `.zip` for asking for help in an issue, forum, or
206206
- `galaid-report.md`
207207
- `manifest.json`
208208
- `file-manifest.json`, including package/image preflight signal samples when available
209+
- `package-previews.md`, a human-readable package/image preflight summary
209210
- `package-previews.json`, a compact view of archive/disc-image launch, install-media, and repair clues
210211
- `environment-checks.json`
211212
- `roadmap.json`

README.zh-CN.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ GalAid 会把这些情况整理成一份小白也能看懂的诊断路线。
4848
- 桌面 beta 支持原生文件夹选择和递归扫描
4949
- 桌面包/镜像预检可读取 ZIP 目录、通过内置或本机 7z 工具列出 RAR/7z/LZH/LHA/ARJ/CAB/TAR 类元数据,并把启动、安装盘和运行库修复样例单独高亮;同时识别镜像介质线索、`autorun.inf + Start.exe + data1.cab` 这类老安装盘结构和包内运行库修复项;用户点击一键启动后可本地解压、挂载/解包镜像、自动重扫并启动推荐入口;如果没有游戏主程序但有 `setup.exe/autorun.exe/.msi``autorun.inf` 指向的安装入口,则单独走安装盘入口
5050
- 复制或下载 Markdown 报告
51-
- 导出包含路线、环境检查、配方命中、文件清单和 `package-previews.json` 包/镜像预检摘要的求助包
51+
- 导出包含路线、环境检查、配方命中、文件清单、`package-previews.md` `package-previews.json` 包/镜像预检摘要的求助包
5252

5353
## 诊断语言
5454

docs/DESKTOP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ During native scanning, GalAid reads `.exe/.com` headers and labels DOS COM/MZ,
88

99
The profile tab can create a `.lnk` shortcut for normal executable entries. GalAid also keeps a small local recent-launch history with entry names, relative paths, and timestamps so users can see what they tried without adding those details to reports or support bundles.
1010

11-
The launch tab can prepare ZIP/RAR/7z archives, legacy LZH/LHA/ARJ/CAB packages, tar-style packages, and detected self-extracting EXE packages through the one-stop launch button. GalAid creates a fresh sibling `*-prepared` folder, asks for a user-provided password only when needed, expands compressed tar packages such as `.tar.gz/.tgz` in a second pass when an inner `.tar` appears, scans the extracted folder, and launches the top recommended entry when one is found. Self-extracting EXE support is metadata-gated: the desktop scanner must first list the EXE with the bundled or local 7z-compatible helper and see archive entries inside it, so ordinary game launchers stay on the direct-launch route. If the prepared content has no game launcher but does expose `setup.exe`, `install.exe`, `autorun.exe`, `.msi`, or an `autorun.inf` target such as `Start.exe` / `SetupJP.exe` / `Setup.cmd`, including shell install commands, GalAid presents that as a separate install-media entry and can open it from the same one-stop flow. Metadata preflight also recognizes old install-disc layouts such as `autorun.inf + Start.exe + data1.cab`, so likely autorun stubs are not promoted as game launchers before extraction. Windows Installer packages are launched through `msiexec.exe /i`. Package preflight also labels bundled DirectX/VC++/.NET/VB6/QuickTime/RPG Maker RTP repair tools so they are treated as fix clues rather than launch entries, and the package card shows launch, install-media, and runtime-repair samples separately from generic file samples. Support bundles include the same compact package/image preflight view as `package-previews.json`. The package tab still offers `Extract and rescan` for users who want to choose the output parent folder manually.
11+
The launch tab can prepare ZIP/RAR/7z archives, legacy LZH/LHA/ARJ/CAB packages, tar-style packages, and detected self-extracting EXE packages through the one-stop launch button. GalAid creates a fresh sibling `*-prepared` folder, asks for a user-provided password only when needed, expands compressed tar packages such as `.tar.gz/.tgz` in a second pass when an inner `.tar` appears, scans the extracted folder, and launches the top recommended entry when one is found. Self-extracting EXE support is metadata-gated: the desktop scanner must first list the EXE with the bundled or local 7z-compatible helper and see archive entries inside it, so ordinary game launchers stay on the direct-launch route. If the prepared content has no game launcher but does expose `setup.exe`, `install.exe`, `autorun.exe`, `.msi`, or an `autorun.inf` target such as `Start.exe` / `SetupJP.exe` / `Setup.cmd`, including shell install commands, GalAid presents that as a separate install-media entry and can open it from the same one-stop flow. Metadata preflight also recognizes old install-disc layouts such as `autorun.inf + Start.exe + data1.cab`, so likely autorun stubs are not promoted as game launchers before extraction. Windows Installer packages are launched through `msiexec.exe /i`. Package preflight also labels bundled DirectX/VC++/.NET/VB6/QuickTime/RPG Maker RTP repair tools so they are treated as fix clues rather than launch entries, and the package card shows launch, install-media, and runtime-repair samples separately from generic file samples. Support bundles include the same package/image preflight view as human-readable `package-previews.md` and compact `package-previews.json`. The package tab still offers `Extract and rescan` for users who want to choose the output parent folder manually.
1212

1313
Disc-image rows can use `Mount/extract and rescan`. On Windows, `.iso` files are mounted with the built-in `Mount-DiskImage` command when available. During scanning, small `.cue` sheets are read for `FILE` track declarations so differently named `.bin/.img` media files can be grouped with the descriptor and missing tracks can be called out before preparation. When the bundled or local 7z-compatible helper can list a supported disc-image directory, GalAid records internal launch, installer, runtime repair, and engine clues without extracting the image. Other supported image files are handled as a best-effort local extraction through the same helper before GalAid rescans the prepared output folder.
1414

src/app.js

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6786,6 +6786,7 @@ function buildSupportBundle(analysis, errorText, language = getAssistantLanguage
67866786
const manifest = buildSupportManifest(analysis, title, generatedAt, language);
67876787
const fileManifest = buildFileManifest(analysis);
67886788
const packagePreviewReport = buildPackagePreviewsReport(analysis);
6789+
const packagePreviewMarkdown = buildPackagePreviewsMarkdown(analysis, language);
67896790
const errorRecipeReport = {
67906791
schema: "galaid.errorRecipes.v1",
67916792
hasErrorText: analysis.errorDiagnostics.hasText,
@@ -6851,6 +6852,11 @@ function buildSupportBundle(analysis, errorText, language = getAssistantLanguage
68516852
content: JSON.stringify(fileManifest, null, 2),
68526853
type: "application/json;charset=utf-8",
68536854
},
6855+
{
6856+
path: "package-previews.md",
6857+
content: packagePreviewMarkdown,
6858+
type: "text/markdown;charset=utf-8",
6859+
},
68546860
{
68556861
path: "package-previews.json",
68566862
content: JSON.stringify(packagePreviewReport, null, 2),
@@ -7009,6 +7015,7 @@ function buildSupportReadme(analysis, title, generatedAt, language = getAssistan
70097015
"- galaid-report.md: human-readable diagnosis",
70107016
"- manifest.json: bundle summary",
70117017
"- file-manifest.json: sanitized file list metadata",
7018+
"- package-previews.md: human-readable archive/disc-image preflight clues",
70127019
"- package-previews.json: archive/disc-image preflight launch, installer, and repair clues",
70137020
"- environment-checks.json: environment checklist",
70147021
"- runtime-repairs.json: bundled runtime repair tool hints",
@@ -7212,6 +7219,102 @@ function buildPackagePreviewsReport(analysis) {
72127219
};
72137220
}
72147221

7222+
function buildPackagePreviewsMarkdown(analysis, language = getAssistantLanguage()) {
7223+
const copies = {
7224+
"zh-CN": {
7225+
title: "包/镜像预检摘要",
7226+
none: "当前没有包/镜像预检数据。",
7227+
overview: "总览",
7228+
count: "预检条目",
7229+
packagePath: "包/镜像",
7230+
format: "格式",
7231+
status: "状态",
7232+
summary: "判断",
7233+
nextStep: "下一步",
7234+
launch: "启动线索",
7235+
installer: "安装/介质线索",
7236+
repair: "运行库修复项",
7237+
engine: "引擎/结构线索",
7238+
warnings: "提醒",
7239+
samples: "样例文件",
7240+
},
7241+
en: {
7242+
title: "Package/Image Preflight Summary",
7243+
none: "No package or image preflight data is available.",
7244+
overview: "Overview",
7245+
count: "Previews",
7246+
packagePath: "Package/image",
7247+
format: "Format",
7248+
status: "Status",
7249+
summary: "Summary",
7250+
nextStep: "Next step",
7251+
launch: "Launch clues",
7252+
installer: "Install/media clues",
7253+
repair: "Runtime repair clues",
7254+
engine: "Engine/structure clues",
7255+
warnings: "Warnings",
7256+
samples: "Sample files",
7257+
},
7258+
ja: {
7259+
title: "パッケージ/イメージ事前チェック概要",
7260+
none: "パッケージ/イメージの事前チェックデータはありません。",
7261+
overview: "概要",
7262+
count: "事前チェック件数",
7263+
packagePath: "パッケージ/イメージ",
7264+
format: "形式",
7265+
status: "状態",
7266+
summary: "判定",
7267+
nextStep: "次の手順",
7268+
launch: "起動手がかり",
7269+
installer: "インストール/メディア手がかり",
7270+
repair: "ランタイム修復候補",
7271+
engine: "エンジン/構造の手がかり",
7272+
warnings: "注意",
7273+
samples: "サンプルファイル",
7274+
},
7275+
};
7276+
const copy = copies[language] || copies["zh-CN"];
7277+
const report = buildPackagePreviewsReport(analysis);
7278+
const lines = [`# ${copy.title}`, ""];
7279+
7280+
lines.push(`## ${copy.overview}`);
7281+
lines.push(`- ${copy.count}: ${formatNumber(report.count)}`);
7282+
lines.push(`- ${copy.launch}: ${formatNumber(report.launchClueCount)}`);
7283+
lines.push(`- ${copy.installer}: ${formatNumber(report.installerClueCount)}`);
7284+
lines.push(`- ${copy.repair}: ${formatNumber(report.runtimeRepairClueCount)}`);
7285+
7286+
if (!report.entries.length) {
7287+
lines.push("");
7288+
lines.push(copy.none);
7289+
return lines.join("\n");
7290+
}
7291+
7292+
for (const [index, entry] of report.entries.entries()) {
7293+
lines.push("");
7294+
lines.push(`## ${index + 1}. ${entry.packagePath || entry.format}`);
7295+
lines.push(`- ${copy.packagePath}: ${entry.packagePath || "-"}`);
7296+
lines.push(`- ${copy.format}: ${entry.groupFormat || entry.format || "-"} (${entry.packageType})`);
7297+
lines.push(`- ${copy.status}: ${entry.status || "-"}`);
7298+
if (entry.summary) lines.push(`- ${copy.summary}: ${entry.summary}`);
7299+
if (entry.nextStep) lines.push(`- ${copy.nextStep}: ${entry.nextStep}`);
7300+
appendPackagePreviewMarkdownList(lines, copy.launch, entry.launchSamples);
7301+
appendPackagePreviewMarkdownList(lines, copy.installer, entry.installerSamples);
7302+
appendPackagePreviewMarkdownList(lines, copy.repair, entry.runtimeRepairSamples);
7303+
appendPackagePreviewMarkdownList(lines, copy.engine, (entry.engineHints || []).map((hint) => `${hint.name} (${hint.count})`));
7304+
appendPackagePreviewMarkdownList(lines, copy.warnings, entry.warnings);
7305+
appendPackagePreviewMarkdownList(lines, copy.samples, (entry.sampleFiles || []).map((file) => `${file.path} (${file.sizeLabel})`), 6);
7306+
}
7307+
7308+
return lines.join("\n");
7309+
}
7310+
7311+
function appendPackagePreviewMarkdownList(lines, label, values, limit = 4) {
7312+
const items = (values || []).filter(Boolean).slice(0, limit);
7313+
if (!items.length) return;
7314+
lines.push(`- ${label}:`);
7315+
for (const item of items) lines.push(` - ${item}`);
7316+
}
7317+
72157318
function buildPackagePreviewManifestEntry(set) {
72167319
const preview = set.archivePreview;
72177320
return {

tests/galaid-smoke.spec.js

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ test("sample diagnosis renders roadmap and support bundle metadata", async ({ pa
4141
await expect(page.locator(".support-file-list")).toContainText("roadmap-checklist.md");
4242
await expect(page.locator(".support-file-list")).toContainText("runtime-repairs.json");
4343
await expect(page.locator(".support-file-list")).toContainText("file-manifest.json");
44+
await expect(page.locator(".support-file-list")).toContainText("package-previews.md");
4445
await expect(page.locator(".support-file-list")).toContainText("package-previews.json");
4546
await expect(page.locator("#supportPanel")).toContainText("诊断摘要");
4647

@@ -56,6 +57,7 @@ test("sample diagnosis renders roadmap and support bundle metadata", async ({ pa
5657

5758
const supportEntries = await page.evaluate(() => buildSupportBundle(currentAnalysis, errorInput.value, "zh-CN").entries.map((entry) => entry.path));
5859
expect(supportEntries).toContain("runtime-repairs.json");
60+
expect(supportEntries).toContain("package-previews.md");
5961
expect(supportEntries).toContain("package-previews.json");
6062
});
6163

@@ -108,18 +110,25 @@ test("package sample shows archive and image preflight without treating it as ru
108110

109111
await page.locator('[data-tab="support"]').click();
110112
await expect(page.locator(".support-file-list")).toContainText("file-manifest.json");
113+
await expect(page.locator(".support-file-list")).toContainText("package-previews.md");
111114
await expect(page.locator(".support-file-list")).toContainText("package-previews.json");
112115

113-
const packagePreviewReport = await page.evaluate(() => {
114-
const entry = buildSupportBundle(currentAnalysis, "", "zh-CN").entries.find((item) => item.path === "package-previews.json");
115-
return JSON.parse(entry.content);
116+
const packagePreviewSupport = await page.evaluate(() => {
117+
const bundle = buildSupportBundle(currentAnalysis, "", "zh-CN");
118+
return {
119+
json: JSON.parse(bundle.entries.find((item) => item.path === "package-previews.json").content),
120+
markdown: bundle.entries.find((item) => item.path === "package-previews.md").content,
121+
};
116122
});
117-
expect(packagePreviewReport.schema).toBe("galaid.packagePreviews.v1");
118-
expect(packagePreviewReport.count).toBeGreaterThanOrEqual(3);
119-
expect(packagePreviewReport.launchClueCount).toBeGreaterThanOrEqual(1);
120-
expect(packagePreviewReport.installerClueCount).toBeGreaterThanOrEqual(1);
121-
expect(packagePreviewReport.runtimeRepairClueCount).toBeGreaterThanOrEqual(1);
122-
expect(packagePreviewReport.entries.some((entry) => entry.launchSamples.includes("SnowTrial/Game.exe"))).toBe(true);
123+
expect(packagePreviewSupport.json.schema).toBe("galaid.packagePreviews.v1");
124+
expect(packagePreviewSupport.json.count).toBeGreaterThanOrEqual(3);
125+
expect(packagePreviewSupport.json.launchClueCount).toBeGreaterThanOrEqual(1);
126+
expect(packagePreviewSupport.json.installerClueCount).toBeGreaterThanOrEqual(1);
127+
expect(packagePreviewSupport.json.runtimeRepairClueCount).toBeGreaterThanOrEqual(1);
128+
expect(packagePreviewSupport.json.entries.some((entry) => entry.launchSamples.includes("SnowTrial/Game.exe"))).toBe(true);
129+
expect(packagePreviewSupport.markdown).toContain("# 包/镜像预检摘要");
130+
expect(packagePreviewSupport.markdown).toContain("SnowTrial/Game.exe");
131+
expect(packagePreviewSupport.markdown).toContain("DXSETUP.exe");
123132

124133
await page.locator("#assistantLanguageSelect").selectOption("en");
125134
await page.locator('[data-tab="report"]').click();

0 commit comments

Comments
 (0)