吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 399|回复: 10
上一主题 下一主题
收起左侧

[其他原创] [cmd]从压缩文件生成可以让wiztree读取的csv文件列表

[复制链接]
跳转到指定楼层
楼主
l2k 发表于 2026-10-4 17:09 回帖奖励

[cmd]从压缩文件生成可以让wiztree读取的csv文件列表

自用软件,不稳定,请酌情修改使用

发帖前要善用【论坛搜索】功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
 楼主| l2k 发表于 2026-10-4 17:10 |楼主
本帖最后由 l2k 于 2026-10-5 16:58 编辑

地址和可能会有的更新
WiztreeTomblist-alpha-v6.2.1.7z SHA-256 2E7399372B2A42C98C60EBBD42E5D013F7C2CA8FEC72F5877C7F3E8037067CF6
WiztreeTomblist-alpha-v6.2.1-[52pj].zip (4.49 KB, 下载次数: 1)
WiztreeTomblist-alpha-v9.7z        SHA-256        4F8D7958F372F3E1C4B3384414B09209385C71AE3267B1A6CE42C72341833F6D
WiztreeTomblist-alpha-v9-[52pj].zip (3.98 KB, 下载次数: 1)
WiztreeTomblist-alpha-v35.7z        SHA-256        4E1B96D7697201044E3C27B8C539AF805C6710CB2B48E21FA2EC0A68E9454D9E
WiztreeTomblist-alpha-v35-[52pj].zip (4.57 KB, 下载次数: 1)
WiztreeTomblist-alpha-v36.7z        SHA-256        BA19093E6C572EF55F79A7C80757F875D0580CD107CFB6A9E691AE8CE1C6A18C
WiztreeTomblist-alpha-v36-[52pj].zip (5.51 KB, 下载次数: 3)
==================
v44 开始需要powershell支持
WiztreeTomblist-alpha-v44.7z        SHA-256        607B2E1C8648970E686A4A7FC51B4EF215E47786D43CE70C806A37721AF2A40B
WiztreeTomblist-alpha-v44-[52pj].zip (7.43 KB, 下载次数: 0)
3#
 楼主| l2k 发表于 2026-10-4 17:11 |楼主
本帖最后由 l2k 于 2026-10-4 17:28 编辑

说明

ArchiveToTomblist

将压缩包转换为 WizTree 格式的 CSV 文件(.tomblist.csv)

⚠️ 风险提示

本脚本曾因 bug 导致文件误删。虽然已修复,但仍建议:

  • 在虚拟机或测试环境中先试用
  • 不要在重要数据目录运行
  • 运行前备份重要文件
  • 如遇异常,立即停止并检查目录

使用本脚本产生的任何数据损失,后果自负


快速开始

1. 准备环境

  • 7-Zip - 必须安装
    • 默认安装路径:C:\Program Files\7-Zip\7z.exe
    • 如果路径不同,编辑 ArchiveToTomblist.bat 第一行修改:
      set "SEVENZIP=你的7z路径"

2. 安装右键菜单

以 管理员身份 运行 InstallContext.bat,选择 1 添加右键菜单

或直接命令行:


InstallContext.bat install

3. 使用

方式一:右键菜单(推荐)

右键点击任何压缩文件(.7z/.zip/.rar 等)
选择"转换为 Tomblist CSV"
自动在同目录生成 文件名.扩展名_.tomblist.csv

方式二:拖拽

将压缩文件拖到 ArchiveToTomblist.bat 上运行

输出格式

生成的 CSV 文件包含:

文件名称 | 大小 | 分配空间 | 修改时间 | 属性 | 是否文件 | 是否文件夹

与 WizTree 3.35 导出格式兼容,可直接在 WizTree 中打开分析
卸载

以 管理员身份 运行 InstallContext.bat,选择 2 删除右键菜单

或直接命令行:
batch

InstallContext.bat uninstall

版本信息

ArchiveToTomblist v6.2.1
InstallContext v1

常见问题

Q: 提示找不到 7z.exe
A: 检查 7-Zip 安装路径,编辑脚本修改 SEVENZIP 变量

Q: 右键菜单没出现
A: 确保以管理员身份运行 InstallContext.bat

Q: 支持哪些格式
A: 所有 7-Zip 支持的格式(.7z, .zip, .rar, .tar, .gz 等)

Made for 52pojie.cn 20261004

4#
 楼主| l2k 发表于 2026-10-4 17:18 |楼主
[Shell] 纯文本查看 复制代码
::ArchiveToTomblist.bat alpha v6.2.1a Made for 52pojie.cn 20261004
@echo off
setlocal EnableExtensions EnableDelayedExpansion

cd /d "%~dp0"
chcp 65001 >nul

set "SEVENZIP=C:\Program Files\7-Zip\7z.exe"

if not exist "%SEVENZIP%" (
    echo 错误:找不到 7z.exe
    pause
    exit /b 1
)

if "%~1"=="" (
    echo 请将压缩文件拖拽到本脚本上。
    pause
    exit /b 1
)

for %%I in (%*) do (
    call :ProcessArchive "%%~fI"
)

pause
exit /b


:ProcessArchive
setlocal EnableExtensions EnableDelayedExpansion

set "ARCHIVE=%~1"

if not exist "%ARCHIVE%" (
    echo 跳过不存在的文件:"%ARCHIVE%"
    endlocal
    exit /b
)

set "ARCHIVE_DIR=%~dp1"
set "ARCHIVE_NAME=%~n1"
set "ARCHIVE_EXT=%~x1"
set "OUTPUT=%ARCHIVE_DIR%%ARCHIVE_NAME%%ARCHIVE_EXT%_.tomblist.csv"

echo.
echo ========== 开始处理 ==========
echo 压缩包:"%ARCHIVE%"

set "TEMP_LIST=%~dp0__7z_list_%RANDOM%_%RANDOM%.txt"
set "TEMP_DATA=%~dp0__data_%RANDOM%_%RANDOM%.txt"

"%SEVENZIP%" l -slt -y "%ARCHIVE%" > "%TEMP_LIST%" 2>&1

if errorlevel 1 (
    echo 错误:7-Zip 无法读取
    endlocal
    exit /b 1
)

echo ✓ 7z 列表已生成

REM 获取当前时间
for /f "tokens=2 delims==" %%A in ('wmic os get localdatetime /format:list') do (
    set "NOW_RAW=%%A"
)
set "NOW=!NOW_RAW:~0,4!/!NOW_RAW:~4,2!/!NOW_RAW:~6,2! !NOW_RAW:~8,2!:!NOW_RAW:~10,2!:!NOW_RAW:~12,2!"

echo 当前时间:"%NOW%"

REM 创建临时数据文件
(echo.) > "%TEMP_DATA%"

REM ===== 解析 7z 输出 =====
set "ITEM_PATH="
set "ITEM_SIZE=0"
set "ITEM_PACKED=0"
set "ITEM_MODIFIED="
set "ITEM_ATTR=32"
set "ROW_COUNT=0"
set "SKIP_FIRST=1"

echo 开始解析 7z 输出...

for /f "usebackq delims=" %%L in ("%TEMP_LIST%") do (
    set "LINE=%%L"
    
    REM 检测 Path 行(新条目开始)
    if "!LINE:~0,5!"=="Path " (
        if defined ITEM_PATH (
            if !SKIP_FIRST! EQU 0 (
                set /a ROW_COUNT+=1
                >> "%TEMP_DATA%" echo !ITEM_PATH!^|!ITEM_SIZE!^|!ITEM_PACKED!^|!ITEM_MODIFIED!^|!ITEM_ATTR!
            ) else (
                set "SKIP_FIRST=0"
            )
        )
        
        REM 提取路径:跳过 "Path = " 部分(7个字符)
        set "ITEM_PATH=!LINE:~7!"
        
        set "ITEM_SIZE=0"
        set "ITEM_PACKED=0"
        set "ITEM_MODIFIED="
        set "ITEM_ATTR=32"
    )
    
    if "!LINE:~0,5!"=="Size " (
        REM 提取大小:跳过 "Size = "
        set "ITEM_SIZE=!LINE:~7!"
        set "ITEM_SIZE=!ITEM_SIZE: =!"
    )
    
    if "!LINE:~0,12!"=="Packed Size " (
        REM 提取压缩大小:跳过 "Packed Size = "
        set "ITEM_PACKED=!LINE:~14!"
        set "ITEM_PACKED=!ITEM_PACKED: =!"
    )
    
    if "!LINE:~0,9!"=="Modified " (
        REM 提取修改时间:跳过 "Modified = "
        set "ITEM_MODIFIED=!LINE:~11!"
        REM 提取前 19 字符(格式:2026-08-23 14:49:00)
        set "ITEM_MODIFIED=!ITEM_MODIFIED:~0,19!"
        REM 替换 - 为 /
        set "ITEM_MODIFIED=!ITEM_MODIFIED:-=/!"
    )
    
    if "!LINE:~0,11!"=="Attributes " (
        REM 提取属性:跳过 "Attributes = "
        set "ATTR_VAL=!LINE:~13!"
        set "ATTR_VAL=!ATTR_VAL: =!"
        if "!ATTR_VAL!"=="D" (
            set "ITEM_ATTR=16"
        ) else (
            set "ITEM_ATTR=32"
        )
    )
)

REM 最后一项
if defined ITEM_PATH (
    if !SKIP_FIRST! EQU 0 (
        set /a ROW_COUNT+=1
        >> "%TEMP_DATA%" echo !ITEM_PATH!^|!ITEM_SIZE!^|!ITEM_PACKED!^|!ITEM_MODIFIED!^|!ITEM_ATTR!
    )
)

echo 解析完成:!ROW_COUNT! 条条目

REM ===== 生成 CSV 输出 =====
echo.
echo 生成 CSV 文件...

(
    echo 生成由 WizTree 3.35 %NOW% (您可以通过捐赠隐藏此信息^)
    echo 文件名称,大小,分配,修改时间,属性,文件,文件夹
) > "%OUTPUT%"

set "CSV_COUNT=0"

for /f "usebackq tokens=1-5 delims=|" %%A in ("%TEMP_DATA%") do (
    set "ITEM_PATH=%%A"
    set "ITEM_SIZE=%%B"
    set "ITEM_PACKED=%%C"
    set "ITEM_MODIFIED=%%D"
    set "ITEM_ATTR=%%E"
    
    if not "!ITEM_PATH!"=="" (
        set /a CSV_COUNT+=1
        
        REM 计算分配空间
        if !ITEM_PACKED! EQU 0 (
            set "ALLOCATED=!ITEM_SIZE!"
        ) else (
            set /a ALLOCATED=^(!ITEM_PACKED! + 4095^) / 4096 * 4096
        )
        
        REM 构建完整路径
        set "FULL_PATH=!ARCHIVE_DIR!!ITEM_PATH!"
        
        REM 目录末尾添加 \
        if !ITEM_ATTR! EQU 16 (
            if "!FULL_PATH:~-1!"=="\" (
                >> "%OUTPUT%" echo."!FULL_PATH!",!ITEM_SIZE!,!ALLOCATED!,!ITEM_MODIFIED!,!ITEM_ATTR!,0,0
            ) else (
                >> "%OUTPUT%" echo."!FULL_PATH!\",!ITEM_SIZE!,!ALLOCATED!,!ITEM_MODIFIED!,!ITEM_ATTR!,0,0
            )
        ) else (
            >> "%OUTPUT%" echo."!FULL_PATH!",!ITEM_SIZE!,!ALLOCATED!,!ITEM_MODIFIED!,!ITEM_ATTR!,0,0
        )
    )
)

echo CSV 行数:!CSV_COUNT!

REM ===== 清理 =====
if exist "%TEMP_LIST%" del /f /q "%TEMP_LIST%"
if exist "%TEMP_DATA%" del /f /q "%TEMP_DATA%"

echo.
echo ========== 完成 ==========
echo 输出文件:"%OUTPUT%"

endlocal
exit /b
5#
 楼主| l2k 发表于 2026-10-4 17:19 |楼主
[Shell] 纯文本查看 复制代码
::InstallContext.bat alpha v1.2 Made for 52pojie.cn 20261004
@echo off
setlocal EnableExtensions EnableDelayedExpansion

REM === 检查参数 ===
if "%~1"=="" goto :ShowMenu
if /i "%~1"=="install" goto :Install
if /i "%~1"=="uninstall" goto :Uninstall
goto :ShowMenu


:ShowMenu
cls
echo.
echo ===========================
echo   ArchiveToTomblist 右键菜单
echo ===========================
echo.
echo 请选择功能:
echo.
echo   1. 添加到右键菜单
echo   2. 从右键菜单删除
echo.
set /p choice="请输入 1 或 2,然后按 Enter: "

if "%choice%"=="1" goto :Install
if "%choice%"=="2" goto :Uninstall

echo 输入无效
pause
exit /b


:Install
cls
echo.
echo === 添加到右键菜单 ===
echo.

REM 检查是否以管理员身份运行
net session >nul 2>&1
if errorlevel 1 (
    echo 错误:需要管理员权限
    echo.
    echo 请右键点击 CMD 或 PowerShell,选择"以管理员身份运行"
    echo 然后运行此脚本
    pause
    exit /b 1
)

REM 获取 ArchiveToTomblist.bat 的完整路径
set "TARGET_SCRIPT=%~dp0ArchiveToTomblist.bat"

if not exist "%TARGET_SCRIPT%" (
    echo 错误:找不到 ArchiveToTomblist.bat
    echo 预期位置:%TARGET_SCRIPT%
    pause
    exit /b 1
)

echo 目标脚本:%TARGET_SCRIPT%
echo.
echo 正在添加右键菜单项...
echo.

REM 注册表路径
set "REG_PATH=HKCU\Software\Classes\*\shell\ArchiveToTomblist"
set "REG_CMD=%REG_PATH%\command"

REM 添加菜单项
reg add "%REG_PATH%" /ve /d "生成 Tomblist CSV" /f >nul 2>&1
if errorlevel 1 (
    echo ? 添加菜单项失败
    pause
    exit /b 1
)

REM ============================================
REM 处理图标
REM ============================================

set "ICON_FILE=%SCRIPT_DIR%\1.ico"

REM 检查本地 1.ico 是否存在
if exist "%ICON_FILE%" (
    reg add "%REG_PATH%" /v Icon /d "%ICON_FILE%,0" /f >nul 2>&1
    echo [1] 已使用本地图标:%ICON_FILE%
) else (
    REM 直接使用系统 shell32.dll 的完整路径(所有 Windows 版本通用)
    reg add "%REG_PATH%" /v Icon /d "C:\Windows\System32\shell32.dll,19" /f >nul 2>&1
    echo [1] 已使用系统图标
)


REM 添加命令
reg add "%REG_CMD%" /ve /d "\"%TARGET_SCRIPT%\" \"%%1\"" /f >nul 2>&1
if errorlevel 1 (
    echo ? 添加命令失败
    pause
    exit /b 1
)

echo ? 成功添加到右键菜单
echo.
echo 现在可以:
echo   1. 右键点击任何压缩文件(.7z, .zip, .rar 等)
echo   2. 选择"转换为 Tomblist CSV"
echo   3. 自动生成 .tomblist.csv 文件
echo.

pause
exit /b


:Uninstall
cls
echo.
echo === 从右键菜单删除 ===
echo.

REM 检查是否以管理员身份运行
net session >nul 2>&1
if errorlevel 1 (
    echo 错误:需要管理员权限
    pause
    exit /b 1
)

echo 正在删除右键菜单项...
echo.

set "REG_PATH=HKCU\Software\Classes\*\shell\ArchiveToTomblist"

reg delete "%REG_PATH%" /f >nul 2>&1

if errorlevel 1 (
    echo ? 删除失败
    pause
    exit /b 1
)

echo ? 已从右键菜单删除
echo.

pause
exit /b

免费评分

参与人数 1吾爱币 +5 热心值 +1 收起 理由
苏紫方璇 + 5 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

6#
zhazhihui 发表于 2026-10-5 03:41
很有深度的帖子,学习了。
7#
fxcfasd 发表于 2026-10-5 06:24
具体的使用场景有哪些
8#
alexccczxty555 发表于 2026-10-5 09:02
这个可以收藏一下。谢谢。
9#
xiaoxiaozhao 发表于 2026-10-5 09:39
学习了解了解
10#
 楼主| l2k 发表于 2026-10-5 17:00 |楼主
[PowerShell] 纯文本查看 复制代码
#Requires -Version 5.1
<#
.SYNOPSIS
    ArchiveToTomblist.ps1 v44-ps - WizTree Tomblist Generator
.DESCRIPTION
    适配 DOpus {allfilepath};修复 StrictMode 下 $null.Count 崩溃;
    UTF-8 管道防乱码;最长公共前缀推导;进程句柄安全释放。
.NOTES
    Version: 43-ps
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory = $true, Position = 0, ValueFromRemainingArguments = $true)]
    [string[]]$ArchivePaths,

    [string]$SevenZipPath = "C:\Program Files\7-Zip\7z.exe"
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'


# ============================================================================
# BLOCK 0: DOpus {allfilepath} 兼容解析 (v43-fix2)
# 说明: 四级降级策略,新增 Level 0 正则提取 Windows 绝对路径
#   Level 0: 从混合字符串中正则提取所有 X:\...\ext 路径 (无视分隔符格式)
#   Level 1: 标准双引号拼接 "C:\a.zip" "C:\b.zip"
#   Level 2: 换行符分隔
#   Level 3: 原始数组已正确拆分
# ============================================================================
$SafePaths = [System.Collections.ArrayList]::new()

if ($null -ne $ArchivePaths) {
    $joinedRaw = $ArchivePaths -join "`n"

    # Level 0: 正则提取所有 Windows 绝对路径 (最可靠,不依赖分隔符)
    # 匹配: 盘符:\任意非空字符直到遇到常见压缩包扩展名
    $pathPattern = '[A-Za-z]:\\[^\r\n"]+?\.(?:zip|7z|rar|tar|gz|bz2|xz|iso|cab|lzma|wim|esd)'
    $regexMatches = [regex]::Matches($joinedRaw, $pathPattern, 'IgnoreCase')

    if ($regexMatches.Count -gt 1) {
        # 提取到多个路径 → 确认是拼接字符串,逐条加入
        foreach ($m in $regexMatches) {
            [void]$SafePaths.Add($m.Value.Trim())
        }
    } elseif ($joinedRaw -match '"\s+"') {
        # Level 1: 标准双引号拼接
        $split = $joinedRaw -split '"\s+"' | Where-Object { $_ -ne '' } | ForEach-Object { $_.Trim('"').Trim() }
        foreach ($s in $split) { [void]$SafePaths.Add($s) }
    } elseif ($joinedRaw -match '[\r\n]+') {
        # Level 2: 换行符分隔
        $split = $joinedRaw -split '[\r\n]+' | Where-Object { $_ -ne '' } | ForEach-Object { $_.Trim().Trim('"') }
        foreach ($s in $split) { [void]$SafePaths.Add($s) }
    } else {
        # Level 3: 单路径或已正确拆分的数组
        foreach ($p in $ArchivePaths) {
            $cleaned = $p.Trim().Trim('"')
            if (-not [string]::IsNullOrWhiteSpace($cleaned)) {
                [void]$SafePaths.Add($cleaned)
            }
        }
    }
}

$ArchivePaths = [string[]]@($SafePaths.ToArray())

# 调试输出 (验证通过后删除)
Write-Host "[DEBUG] Parsed $($ArchivePaths.Count) path(s):" -ForegroundColor DarkGray
foreach ($dp in $ArchivePaths) { Write-Host "  -> $dp" -ForegroundColor DarkGray }


# ============================================================================
# BLOCK 1: 环境预检与全局变量初始化
# 说明: 校验 7z 路径,初始化时间戳与 UTF-8 NoBOM 编码器
# ============================================================================
if (-not (Test-Path -LiteralPath $SevenZipPath)) {
    Write-Error "[ERROR] 7z.exe not found at: $SevenZipPath"
    return
}

$NowStr      = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
$utf8NoBom   = New-Object System.Text.UTF8Encoding $false


# ============================================================================
# BLOCK 2: 主循环 - 逐包处理入口
# 说明: 遍历每个压缩包,解析路径并生成输出文件名
# ============================================================================
foreach ($Archive in $ArchivePaths) {
    try {
        $FullArchivePath = [System.IO.Path]::GetFullPath($Archive)
    } catch {
        Write-Warning "[SKIP] Invalid path: $Archive"
        continue
    }

    if (-not (Test-Path -LiteralPath $FullArchivePath)) {
        Write-Warning "[SKIP] File missing: $FullArchivePath"
        continue
    }

    $ArchiveDir      = [System.IO.Path]::GetDirectoryName($FullArchivePath)
    $ArchiveBaseName = [System.IO.Path]::GetFileNameWithoutExtension($FullArchivePath)
    $ArchiveExt      = [System.IO.Path]::GetExtension($FullArchivePath)
    $OutputFile      = Join-Path $ArchiveDir "$ArchiveBaseName${ArchiveExt}.tomblist.csv"
    $VRootName       = "$ArchiveBaseName\"

    Write-Host "`n===== Processing =====" -ForegroundColor Cyan
    Write-Host "Input : $FullArchivePath"
    Write-Host "Output: $OutputFile"


    # ========================================================================
    # BLOCK 3: 调用 7z 并流式解析记录
    # 说明: UTF-8 管道编码;状态机逐行解析;try/finally 保证句柄释放
    # ========================================================================
    $psi = New-Object System.Diagnostics.ProcessStartInfo
    $psi.FileName               = $SevenZipPath
    $psi.Arguments              = "l -slt -sccUTF-8 -scsUTF-8 -y `"$FullArchivePath`""
    $psi.RedirectStandardOutput = $true
    $psi.RedirectStandardError  = $true
    $psi.UseShellExecute        = $false
    $psi.CreateNoWindow         = $true
    $psi.StandardOutputEncoding = [System.Text.Encoding]::UTF8

    $process = $null
    $Records = [System.Collections.Generic.List[PSCustomObject]]::new()

    try {
        $process = [System.Diagnostics.Process]::Start($psi)
        $CurrentRecord = @{}
        $reader = $process.StandardOutput

        while ($null -ne ($line = $reader.ReadLine())) {
            # 空行 = 记录分隔符
            if ([string]::IsNullOrWhiteSpace($line)) {
                if ($CurrentRecord.Count -gt 0 -and $CurrentRecord.ContainsKey('Path')) {
                    $sizeVal   = [uint64]0
                    $packedVal = [uint64]0
                    if ($CurrentRecord.ContainsKey('Size'))        { [uint64]::TryParse($CurrentRecord['Size'],        [ref]$sizeVal)   | Out-Null }
                    if ($CurrentRecord.ContainsKey('Packed Size')) { [uint64]::TryParse($CurrentRecord['Packed Size'], [ref]$packedVal) | Out-Null }

                    $isFolder = ($CurrentRecord.ContainsKey('Folder')     -and $CurrentRecord['Folder'] -eq '+') -or
                                ($CurrentRecord.ContainsKey('Attributes') -and $CurrentRecord['Attributes'] -match '^D')

                    $modStr = $NowStr
                    if ($CurrentRecord.ContainsKey('Modified') -and -not [string]::IsNullOrWhiteSpace($CurrentRecord['Modified'])) {
                        $modStr = $CurrentRecord['Modified'].Replace('-', '/')
                        if ($modStr.Length -gt 19) { $modStr = $modStr.Substring(0, 19) }
                    }

                    $Records.Add([PSCustomObject]@{
                        RawPath    = $CurrentRecord['Path']
                        Size       = $sizeVal
                        PackedSize = $packedVal
                        Modified   = $modStr
                        Attributes = if ($isFolder) { 16 } else { 32 }
                        IsFolder   = $isFolder
                    })
                }
                $CurrentRecord = @{}
                continue
            }

            # Key=Value 解析
            $eqIndex = $line.IndexOf('=')
            if ($eqIndex -gt 0) {
                $key = $line.Substring(0, $eqIndex).Trim()
                $val = $line.Substring($eqIndex + 1).TrimStart()
                $CurrentRecord[$key] = $val
            }
        }

        $process.WaitForExit()
        if ($process.ExitCode -ne 0) {
            $err = $process.StandardError.ReadToEnd()
            Write-Warning "[WARN] 7z exited with code $($process.ExitCode): $err"
        }
    } finally {
        if ($null -ne $process) { $process.Dispose() }
    }

    if ($Records.Count -eq 0) {
        Write-Warning "[SKIP] No valid records parsed from archive."
        continue
    }


    # ========================================================================
    # BLOCK 4: 最长公共前缀推导
    # 说明: @() 强制包裹过滤结果,防止 StrictMode 下 $null.Count 崩溃
    #       逐级比对所有路径,提取最长公共父目录作为 VRoot 基准
    # ========================================================================
    $CommonPrefix = ""
    $allPaths = @($Records | Where-Object { -not [string]::IsNullOrWhiteSpace($_.RawPath) } | Select-Object -ExpandProperty RawPath)

    if ($allPaths.Count -gt 0) {
        $parts = $allPaths[0] -split '\\'
        for ($i = 0; $i -lt $parts.Count; $i++) {
            $candidate = ($parts[0..$i] -join '\') + '\'
            $allMatch = $true
            foreach ($p in $allPaths) {
                if (-not $p.StartsWith($candidate, [System.StringComparison]::OrdinalIgnoreCase)) {
                    $allMatch = $false
                    break
                }
            }
            if ($allMatch) { $CommonPrefix = $candidate } else { break }
        }
    }
    Write-Host "Detected Prefix: '$CommonPrefix'"


# ============================================================================
# BLOCK 5: 构建 CSV 内容 (v43-fix4 + __TOMBSTONE__ 路径嵌入)
# 说明: 将 __TOMBSTONE__ 作为虚拟子目录嵌入 VRootName 路径结构
#       输出格式: "VRootName\__TOMBSTONE__\原始绝对路径"
# ============================================================================
$sb = [System.Text.StringBuilder]::new()
[void]$sb.AppendLine("Generated by WizTree Tomblist Tool v43-ps")
[void]$sb.AppendLine('File Name,Size,Allocated,Modified,Attributes,Files,Folders')
[void]$sb.AppendLine("`"$VRootName`",0,0,$NowStr,0,0,0")

$fileCount = 0
$dirCount  = 0
$seenDirs  = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)

$archiveSelfName = "$ArchiveBaseName$ArchiveExt"

foreach ($rec in $Records) {
    $relPath = $rec.RawPath
    if ($CommonPrefix -and $relPath.StartsWith($CommonPrefix, [System.StringComparison]::OrdinalIgnoreCase)) {
        $relPath = $relPath.Substring($CommonPrefix.Length)
    }
    if ([string]::IsNullOrWhiteSpace($relPath)) { continue }

    if ($rec.IsFolder) {
        $dirPath = $relPath.TrimEnd('\', '.')
        if ([string]::IsNullOrWhiteSpace($dirPath)) { continue }
        if ($seenDirs.Add($dirPath)) {
            [void]$sb.AppendLine("`"$VRootName$dirPath\`",0,0,$NowStr,16,0,0")
            $dirCount++
        }
    } else {
        $filePath = $relPath.TrimEnd('\', '.')
        if ([string]::IsNullOrWhiteSpace($filePath)) { continue }
        $alloc = if ($rec.Size -gt 0) { $rec.Size } else { $rec.PackedSize }

        # ★ 判断是否为压缩包自身记录
        $isArchiveSelf = ($filePath -eq $archiveSelfName) -or
                         ($filePath.EndsWith("\$archiveSelfName", [System.StringComparison]::OrdinalIgnoreCase)) -or
                         ($rec.RawPath -eq "$ArchiveBaseName$ArchiveExt") -or
                         ([System.IO.Path]::GetFileName($rec.RawPath) -eq $archiveSelfName -and
                          -not ($CommonPrefix -and $rec.RawPath.StartsWith($CommonPrefix, [System.StringComparison]::OrdinalIgnoreCase)))

        # ★ 核心改动: __TOMBSTONE__ 作为虚拟子目录嵌入路径
        #   注意: 必须用 ${VRootName} 而非 $VRootName,否则 PS 会将
        #         $VRootName__TOMBSTONE__ 整体解析为一个未定义的变量名
        $displayName = if ($isArchiveSelf) {
            "${VRootName}__TOMBSTONE__\$($rec.RawPath)"
        } else {
            "${VRootName}$filePath"
        }

        [void]$sb.AppendLine("`"$displayName`",$($rec.Size),$alloc,$($rec.Modified),$($rec.Attributes),0,0")
        $fileCount++
    }
}



    # ========================================================================
    # BLOCK 6: 写入 UTF-8 NoBOM 文件并输出统计
    # 说明: 使用 .NET API 直接写入,绕过 PowerShell 默认 BOM 行为
    # ========================================================================
    [System.IO.File]::WriteAllText($OutputFile, $sb.ToString(), $utf8NoBom)

    Write-Host "[OK] Generated: $OutputFile" -ForegroundColor Green
    Write-Host "     Files: $fileCount | Dirs: $dirCount"
}

Write-Host "`n[DONE] All tasks finished." -ForegroundColor Yellow
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-10-5 18:46

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表