简介面向需要解析网易 NeoX 引擎资源包的游戏逆向与 Mod 爱好者这套工具聚焦 Eve Echoes2020 年初版本所用 NPK 格式可读取包内文件列表并用 LZ4 解压为每个文件猜测 MIME 类型并补全扩展名方便后续查看与再处理。压缩包共 15 个文件、约 24KB以 Python 脚本为主体承担 pyc 反编译、解密与 pymarshal 解析等辅助流程Rust 源码与 Cargo 配置构成主程序配合 README 说明构建方式shell 脚本和许可证等杂项文件使工程结构完整。目前已有 5094 人学习下载。读者可直接获得一套可编译运行的 NPK 解包工具链以及脚本重定向、资源解包与反编译的参考实现作者还列出了 RC4 加密、简单加密与 NPK 重打包的后续计划便于按需扩展或二次开发。1. 从打不开的 .npk 说起neox-tools 能替你省掉哪些手工活neox-tools 是处理 NeoX 引擎 NPK 资源包的 Python 工具核心就两件事把 .npk 拆开、把改过的目录重新封回去。你拿到一个几十兆的 NPK双击打不开拖进常见压缩软件也只看到一坨二进制里面其实是贴图、配置、脚本和索引表用十六进制看像黑匣子。它解决的是“能看见、能改、能回读”这三个动作先列出包内文件再按目录解包改完资源后按原索引重打包最后用解包结果和哈希做回读验证。适合做资源分析、Mod 验证、资源管线排错的人新手可以按命令跑通熟手会关注索引偏移、压缩标志和对齐填充。如果只是想把一张图换掉用不到完整逆向但只要碰到“解包正常、重打包后不认”的情况就得回到 NPK 的二进制结构里看字段。下面按“先读懂头、再跑工具、最后避坑”的顺序拆。2. 拆开 NPK 的二进制骨架魔数、索引表与压缩标志NPK 不是普通压缩包常见结构是“文件头 索引表 数据块”。文件头告诉你包内有多少文件、索引表从哪个偏移开始、索引表有多长索引表里每一项再指向具体数据块并记录原始大小、压缩后大小和压缩类型。不同版本、不同项目的 NPK 字段顺序可能有差异所以第一步不是硬改而是先做只读解析把手上的样本包认清楚。2.1 先看文件头20 字节里藏着什么很多 NPK 样本的前 4 个字节是NXPK魔数后面跟着版本、文件数量、索引偏移和索引长度。下面这个脚本只读文件头不碰数据区适合在动手解包前确认样本是不是同一类结构。import struct from pathlib import Path def read_npk_header(path: str) - dict: p Path(path) with p.open(rb) as f: head f.read(20) # 先读 20 字节常见头长度样本不同就继续往后读 if len(head) 20: raise ValueError(文件太小连文件头都不完整) magic head[0:4] version, file_count, index_offset, index_size struct.unpack(IIII, head[4:20]) return { magic: magic, version: version, file_count: file_count, index_offset: index_offset, index_size: index_size, file_size: p.stat().st_size, } if __name__ __main__: info read_npk_header(./sample.npk) for k, v in info.items(): print(f{k}: {v})这段脚本做三件事读前 20 字节、按小端无符号整数解出四个字段、把文件总大小一并打印。magic不是NXPK时不要急着改脚本先把前 32 字节用十六进制打印出来确认是不是变体头index_offset是索引表起始位置index_size是索引表字节数这两个值决定了后面按偏移读索引时会不会越界。version只用来辅助判断字段布局不要拿它当唯一依据。常见头字段可以按下面这张表对照偏移量以 20 字节头为例样本不一致时以实际十六进制为准偏移长度字段用途0x004magic识别包类型常见为NXPK0x044version辅助判断索引项布局0x084file_count包内文件数量0x0C4index_offset索引表起始偏移0x104index_size索引表字节长度注意先复制一份样本包所有写操作都在副本上做原包只读。2.2 索引项怎么读文件名偏移、数据偏移与压缩类型文件头只是目录封面真正决定文件能不能提出来的是索引项。常见索引项会包含文件名偏移、文件数据偏移、原始大小、压缩后大小、压缩类型和标志位有的版本还会多一个哈希或对齐字段。索引项长度可能是 0x18 或 0x20不要凭感觉写死先用工具的文件列表功能打印几条再对照十六进制看字段跨度。压缩标志是最容易看反的地方。0 通常表示不压缩1 常见是 zlib2 可能是 lz43 可能是 zstd但不同版本枚举值不一定一致。工具里一般会有映射表遇到解出来是乱码时先怀疑压缩类型而不是文件损坏。下面这张表只作为排查顺序不作为唯一标准压缩标志常见含义排查动作0原始数据直接按大小读取检查文件头是否符合格式1zlib/deflate用 zlib 解压后看前几个字节2lz4确认工具是否带 lz4 依赖3zstd确认 zstd 解压参数和窗口大小读索引时不要一次性把整个索引表读进内存再切片先按file_count算一下“索引项长度 × 文件数量”是否接近index_size。如果差得很远说明索引项长度或字段布局判断错了继续硬读只会得到一堆越界偏移。比较稳的做法是先用工具的list子命令打印前 10 个文件名和偏移再拿其中一条去十六进制里定位确认文件名是紧跟在索引表后面还是散落在数据区。2.3 为什么先用只读解析选型理由与边界用 Python 做只读解析的理由很实际改一个字段、打一条日志、对比两个偏移几行代码就能跑不需要编译。NPK 处理里最贵的不是解压算法而是“字段布局判断错”带来的反复试错先把头、索引、压缩标志打印出来比一上来重打包省时间。Python 的struct、zlib、hashlib足够覆盖大部分验证场景lz4 和 zstd 再按需装依赖。边界也要提前说清只读解析能确认魔数、数量、索引偏移和压缩标志但不能替代完整格式文档。遇到加密 NPK、文件名哈希表、二次压缩或自定义混淆工具可能只解出一部分剩下的要靠样本对比。另一个边界是“能解包不等于能重打包”重打包还要处理索引回填、数据对齐和哈希校验这些放到第 4 章。只读阶段的目标只有一个让样本包在工具里稳定列出文件并且解出来的前几个文件能打开。3. 把 neox-tools 跑起来Python 环境、解包命令与输出目录工具跑不起来后面所有分析都是空谈。常见翻车点不是代码多难而是 Python 版本不对、依赖没装全、命令行入口找错。neox-tools 是 Python 工具先确认解释器版本再进仓库目录看入口是python -m还是脚本文件不同仓库组织方式不一样先跑--help比猜命令可靠。3.1 环境准备与依赖安装建议用独立虚拟环境避免把系统的 Python 包搅乱。下面命令在仓库根目录执行requirements.txt不存在时再看pyproject.toml或setup.py不要跳过依赖直接跑主程序。python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip pip install -r requirements.txt python -m neox_tools --help这段命令做四件事建虚拟环境、激活、升级 pip、安装依赖并打印帮助。python3 -m venv .venv里的.venv是目录名可以换成你习惯的名字source在 Windows 下换成对应激活脚本。python -m neox_tools --help如果报“No module named neox_tools”说明当前目录不是仓库根目录或者入口是neox_tools.py这时改用python neox_tools.py --help。依赖里如果出现 lz4、zstd、pycryptodome安装失败先看编译工具和 wheel 是否匹配当前平台不要硬改代码绕过。提示先把--help的输出保存到文本里后面写批量脚本时直接对照子命令和参数名。3.2 解包一个样本包命令、参数与日志确认帮助信息后先列文件再解包。列文件是只读操作能快速判断索引是否正常解包时先只解前几个文件确认输出目录结构和文件名没有异常再全量解。# 列出包内文件先确认索引能读 python -m neox_tools list ./sample.npk # 只解前 20 个文件到 out_preview避免一次写太多 python -m neox_tools unpack ./sample.npk -o ./out_preview --limit 20 # 确认预览目录正常后再全量解包 python -m neox_tools unpack ./sample.npk -o ./out_fulllist子命令通常只读索引不写磁盘unpack的-o指定输出目录--limit是预览限制具体参数名以--help为准没有这个参数就先用list看前几条再全量。输出目录不要选在系统临时目录里后面重打包要按原目录结构回填解包日志里重点看“跳过文件”“解压失败”“文件名冲突”三类信息出现跳过时不要直接全量覆盖先看跳过原因。如果工具没有--limit可以用list的输出重定向到文件再按文件名单独解。解包后的目录一般会保留包内路径路径分隔符可能被统一成当前系统格式重打包前要确认工具是否要求使用原始分隔符。文件名大小写也要留意某些包内同时存在Tex/a.png和tex/A.png在大小写不敏感的文件系统上会互相覆盖。3.3 用脚本验证索引与文件完整性解包完成后不要只看“命令没报错”用脚本对输出目录做一次清单和大小核对。下面脚本统计文件数量、总大小并抽查前 10 个文件是否非空它不依赖工具内部结构适合作为解包后的第一道自检。from pathlib import Path import hashlib root Path(./out_full) files [p for p in root.rglob(*) if p.is_file()] total sum(p.stat().st_size for p in files) print(files:, len(files)) print(total_bytes:, total) for p in files[:10]: data p.read_bytes() print(p.relative_to(root), len(data), hashlib.md5(data).hexdigest()[:8])脚本先递归收集文件再统计数量和总字节数最后对前 10 个文件打印相对路径、大小和 MD5 前 8 位。root换成你的解包目录files[:10]只是抽查不要拿它当完整校验。总大小和包内索引记录的原始大小可以对一下差得太多说明有文件没解出来或解压后大小不对。MD5 只用于快速比对不用于安全场景重打包前后对比同一文件的哈希比肉眼看目录可靠。注意解包目录里不要混入你自己新建的说明文件重打包时可能被一起封进去。4. 改完资源再塞回去重打包流程、对齐与校验重打包比解包更容易翻车因为解包只要读对索引重打包还要把新数据偏移、压缩后大小、索引顺序和文件对齐全部写回去。常见做法是保持原目录结构只替换目标文件不改文件名和路径工具负责重新计算索引但压缩参数和对齐策略要自己确认。下面按“目录约定、打包命令、回读验证”三步走。4.1 替换文件前的目录约定先复制一份解包目录作为工作目录保留原解包结果不动。替换文件时只动目标文件不要顺手改目录名、不要增删空目录、不要把备份文件留在目录里。很多重打包失败不是算法问题而是目录里多了一个.bak或编辑器临时文件被工具当成新资源写进索引导致包内文件数量和原包对不上。目录约定可以按下面几条执行包内路径保持原样文件名大小写不变替换文件的扩展名和原文件一致需要新增文件时先确认工具支持新增索引不支持就只做替换。工作目录建议命名为work_replace和out_full分开出问题可以直接删掉重来。cp -r ./out_full ./work_replace # 只替换目标文件例如把一张贴图换成修改后的版本 cp ./mods/new_icon.png ./work_replace/path/in/package/icon.png这两条命令先复制目录再覆盖单个文件。cp -r保留目录结构第二条命令的目标路径要按解包后的真实相对路径写不要自己拼一个不存在的目录。替换前用file或图片查看器确认新文件能正常打开坏文件封进去后游戏侧只会表现为资源加载失败排查成本更高。4.2 重打包命令与压缩参数重打包命令通常接收目录和输出文件压缩参数按原包样本选择。原包大量文件是 zstd 就优先用 zstd原包是不压缩或 zlib 就不要擅自换算法算法换了工具可能能写出来但目标程序读不读是另一回事。# 先打一个只包含少量文件的小包做验证确认命令和参数可用 python -m neox_tools pack ./work_replace -o ./repacked_test.npk --compress zstd # 小包验证通过后再全量重打包 python -m neox_tools pack ./work_replace -o ./repacked_full.npk --compress zstdpack的第一个参数是工作目录-o是输出文件--compress指定压缩算法。参数名以--help为准没有--compress就不要硬加先看工具默认策略。小包验证的意义是缩短反馈时间如果小包都读不回来全量包不用试。输出文件不要覆盖原包命名带_test、_full这类后缀方便回滚。压缩级别如果有--level先从默认值开始不要一上来拉满压缩级别变化会影响数据大小和对齐。提示重打包日志里重点看“索引回填”“对齐填充”“压缩失败”三类行出现跳过文件时先停下来。4.3 回读验证用解包结果对比哈希重打包完成后必须用工具再解一次新包和work_replace做文件级对比。不要只看文件数量数量对不代表内容对压缩标志写错时文件数量可能完全一致但解出来是乱码。from pathlib import Path import hashlib def digest(p: Path) - str: return hashlib.sha256(p.read_bytes()).hexdigest() left Path(./work_replace) right Path(./out_verify) left_files {p.relative_to(left): digest(p) for p in left.rglob(*) if p.is_file()} right_files {p.relative_to(right): digest(p) for p in right.rglob(*) if p.is_file()} only_left sorted(set(left_files) - set(right_files)) only_right sorted(set(right_files) - set(left_files)) diff sorted(k for k in set(left_files) set(right_files) if left_files[k] ! right_files[k]) print(only_left:, only_left[:10]) print(only_right:, only_right[:10]) print(diff:, diff[:10])这段脚本先对两个目录做相对路径到 SHA-256 的映射再分别求“只在左边”“只在右边”“两边都有但哈希不同”的集合。left是重打包前的工作目录right是新包解包后的目录only_left不为空说明有文件没封进去only_right不为空说明多封了文件diff不为空说明内容或压缩回读不一致。输出只打印前 10 条避免大包刷屏真正排查时把完整列表写到文本里再逐条看。5. 避坑与排查NPK 处理里最容易翻车的五个点NPK 的坑大多不在“代码会不会写”而在“样本和假设对不对”。下面五条是处理 NeoX NPK 时比较常见的翻车记录每条按现象、原因、解决写方便直接对照。5.1 解包报 magic 不匹配先别改代码先看文件头现象是工具一打开就报魔数不对或者list直接退出。原因通常不是工具坏了而是样本包有变体头、外层还有一层封装或者文件在传输时被截断。解决时先用十六进制看前 32 字节确认前 4 字节到底是NXPK还是别的标记再看文件大小是否和来源记录一致。如果前 4 字节是别的标记不要强行把代码里的NXPK改掉先把十六进制贴到样本对比里确认是不是同一种 NPK 的变体。传输截断也会导致头能读、索引越界重新复制一份样本再试比改代码快。5.2 文件列表能读但提取出来是乱码压缩类型和加密标志看反了现象是list能列出文件名unpack也不报错但解出来的图片打不开、配置全是乱码。原因多半是压缩标志映射错了或者把加密数据当成了普通压缩数据。解决时先挑一个解出来的文件看前几个字节PNG 应该以\x89PNG开头文本应该是可读字符对不上就回到索引项确认压缩标志和压缩后大小。工具如果有--raw之类的原始提取参数先用它把数据原样提出来再手动试 zlib、lz4、zstd。不要一次改多个参数每次只改压缩类型改完只解一个文件验证。5.3 重打包后目标程序不认对齐和索引偏移没回填现象是重打包命令成功输出文件大小也正常但目标程序加载失败或者只加载出一部分资源。原因常见于数据块对齐、索引偏移回填、文件顺序变化。解决时先用工具解新包确认能完整解回如果能解回但目标程序不认重点看对齐参数和索引顺序。把原包和新包的文件头、索引表前几项做十六进制对比确认index_offset、index_size和每个数据块偏移是否符合原包规律。重打包时不要随意改变文件顺序替换文件优先保持原索引位置工具支持--align就从原包样本推断对齐值不要默认 0。5.4 文件名大小写或路径分隔符导致覆盖目录约定不一致现象是解包目录里文件数量比包内索引少或者重打包后某些文件变成另一个文件的内容。原因是包内路径大小写不一致落到大小写不敏感的文件系统上互相覆盖路径分隔符被工具统一后重打包时又按当前系统分隔符写回。解决时在解包后先统计索引里的文件名和磁盘上的文件名发现数量不一致就开大小写敏感目录或者先把冲突文件改名保存。重打包前确认工具是否要求使用/作为分隔符不要手工把路径改成反斜杠。目录里不要留编辑器临时文件临时文件被当成资源封进去也会造成“多文件”。5.5 覆盖原包后无法回滚没有做哈希和备份现象是改完直接覆盖原包目标程序不认手里没有可回退的版本。原因是写操作前没有备份也没有记录原包哈希。解决时在第一次解包前就把原包复制到backup/并记录 SHA-256每次重打包输出新文件不覆盖原包。下面命令可以做最小备份和哈希记录mkdir -p backup cp ./sample.npk ./backup/sample.npk.bak sha256sum ./sample.npk ./backup/sample.sha256backup/放在工作目录外层避免被重打包工具扫进去sha256sum在部分平台命令名不同换成对应哈希工具即可。回滚时先比对哈希再替换文件不要凭文件名判断是不是原包。6. 进阶用法批量扫描、差异对比与一条自检习惯批量处理时单包命令不够用得先把目录里的 NPK 扫成清单再挑样本做差异对比。下面脚本只读文件头把路径、魔数、文件数量、索引偏移打到 CSV方便你在几十个包里先筛出异常样本。import csv, struct from pathlib import Path rows [] for p in Path(./npk_dir).rglob(*.npk): with p.open(rb) as f: head f.read(20) if len(head) 20: rows.append([str(p), too_small, , , ]) continue magic head[:4] version, count, idx_off, idx_size struct.unpack(IIII, head[4:20]) rows.append([str(p), magic.hex(), version, count, f{idx_off}:{idx_size}]) with open(./npk_scan.csv, w, newline, encodingutf-8) as f: csv.writer(f).writerows(rows)npk_dir换成你的样本目录*.npk按实际扩展名调整输出 CSV 里魔数用十六进制方便和十六进制编辑器对照。差异对比用cmp或哈希清单都行重点不是工具多高级而是每次重打包后固定跑一遍“解包—清单—哈希”三步。从那以后我每次重打包前都会先跑清单和哈希自检宁可多花两分钟也不吃二进制的后悔药。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
Momenta自动驾驶核心竞争力与商业落地解析 1. Momenta的核心竞争力解析作为自动驾驶领域的头部企业,Momenta的算法实力早已得到行业公认。但真正让这家公司在激烈竞争中脱颖而出的,是其构建的独特商业与技术体系。从实际业务接触来看,他们至少在三个维度建立了难以复制的优势。1.1 数据… · 2026/11/3 15:23:58
NVIDIA AI for Media:从实时字幕到体育转播的GPU智能工作流 去年秋天我参与了一场大型体育赛事转播的POC项目,甲方给出的需求清单里有一条让我印象很深:半场间歇内完成多机位快速回放剪辑,实时字幕延迟压到两秒以内。放在五年前,这个需求几乎只能靠堆人力和专用硬件解决,而现在N… · 2026/11/2 12:43:43
学术写作工具横评:提升论文效率的三大神器 1. 项目概述:写作工具横评的必要性作为一位经历过继续教育论文写作的过来人,我深知选择合适的写作工具对学术产出效率的决定性影响。去年为了完成MBA学位论文,我耗时三个月对市面上主流的12款写作辅助工具进行了深度测试,从基础的… · 2026/11/2 12:43:43
LangChain+ChromaDB 崩溃排查:OOM、段错误与工程化配置 内核掉了。不是抛异常,不是栈回溯,是 Jupyter 的 kernel 直接变灰,或者终端里的 python 进程凭空消失,只留下一行Killed或者什么都没有。我第一次在 LangChain 里挂上 ChromaDB 做本地知识库时,就是这么被教育了一整晚… · 2026/11/3 15:24:57
Sigma-Delta ADC噪声整形:从1位量化到15位精度的系统设计 简介:《高性能sigma-delta ADC的设计与研究》PDF为西安电子科技大学微电子学与固体电子学博士学位论文,面向微电子、模拟集成电路及ADC设计方向的研究生、工程师与科研人员,聚焦过采样和噪声整形机制下的高精度模数转换器系统设计与实现。包内… · 2026/11/3 15:24:57
MCP协议:自描述通信与AI系统集成实践 1. MCP协议的本质与自描述特性 MCP(Model Context Protocol)本质上是一种为AI系统设计的通信协议,它最革命性的突破在于将"自描述"特性深度融入协议设计。这种自描述机制使得协议本身能够携带完整的元数据说明,就像给每… · 2026/11/3 15:24:57
VMware Workstation Pro 17安装与优化指南 1. VMware Workstation Pro 17 核心价值解析 作为业界领先的虚拟化解决方案,VMware Workstation Pro 17在Windows平台上的表现堪称经典。这款软件允许用户在同一台物理机上同时运行多个操作系统实例,从Windows 10/11到各种Linux发行版,甚至是… · 2026/11/3 15:24:57
高频电路选频网络:串联/并联谐振与部分接入解析 简介:这份PPT课件面向电子工程、通信工程等专业的学生与自学者,聚焦高频电路中选频网络的基础理论与串联谐振回路这一核心知识点,适合课堂同步学习、期末复习或考研前的概念梳理。课件共1个文件,为单一PPT格式,压缩包约… · 2026/11/3 15:24:57
从冷启动到传播引爆:一文拆解内容热度背后的buzz机制 这两年不管做内容还是做产品的人,应该都会经历过这种困惑:一条消息发出去,阅读量看着还行,点赞也有,可评论区像集体断网,更没人转发。项目复盘的时候,大家把这个现象叫“有曝光没热度”。后来我… · 2026/11/3 15:23:55
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46
STM32F411RE与MAX14431实现SPI隔离:工业现场抗共模干扰实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
ESP32+大模型AI硬件开发:从Demo到产品的8个工程问题 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
基于MAX14431BAEE+与PIC18F85J10的SPI隔离方案设计与实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 1:01:59
双极步进电机控制实战:DRV8818PWPR与PIC18F86K22驱动调试 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/2 13:10:28
STM32F207ZG + DRV8818PWPR 步进电机驱动实战解析 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 6:04:24
DRV8818PWPR+dsPIC30F4013工业级双极步进电机控制方案 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/11/3 5:52:46