08-architecture-and-scale.md · Cập nhật lần cuối: 2026-09-05 08:45 +07 · sha256 nguồn 220cfd3c2ef608 — Kiến trúc & quy mô
Nguồn: chưng cất từ restore SOT
com.Chodun.CatGunner1.1.59 (BardCat). Đây là tài liệu về hình dạng của một game idle đã ship: nó lớn cỡ nào, được đóng gói ra sao, và những quyết định kiến trúc nào phải trả giá về sau.
1. Câu hỏi tài liệu này trả lời
Ba câu: 1. Một game idle thương mại thực tế lớn đến mức nào, và cấu trúc ra sao? 2. Quyết định "một scene duy nhất" tốn kém những gì? 3. Một studio bảo vệ build của họ như thế nào — và điều đó dạy ta gì về việc ship?
2. Stable truths
2.1 Danh tính build
| Claim | Mức bằng chứng | Nguồn / provenance |
|---|---|---|
com.Chodun.CatGunner, versionName 1.1.59, versionCode 227, build-guid 9a37ce40d5de463b96911d14dd2408ad |
package-source | [shared facts nội bộ] |
| Unity 6000.0.61f1, URP, IL2CPP, chỉ arm64-v8a | package-source | [shared facts nội bộ]; chuỗi build trong libil2cpp.so: C:/Unity_game/6000.0.61f1/Editor/Data/il2cpp/… |
Đóng gói XAPK: base com.Chodun.CatGunner.apk (147 557 129 B) + config.arm64_v8a.apk (40 028 322 B). Không OBB, không asset pack |
package-source | [report: asset_lane.md] §1 |
libil2cpp.so = 101 516 264 B; global-metadata.dat = 17 924 548 B |
package-source | [shared facts nội bộ] |
minSdk 27, targetSdk 36, uses-gl-es 0x30000, vulkan not-required |
package-source | [report: project_lane.md] §7 |
ScriptingAssemblies.json liệt kê 182 assembly |
package-source | [shared facts nội bộ] |
| Sau khi giải mã metadata: 136 assembly · 18 678 type · 141 908 method · 79 216 field · 167 415 param · 22 529 string literal | metadata-proven | [shared facts nội bộ] |
Assembly-CSharp = 731 type / 5 194 method; Assembly-CSharp-firstpass = 104 type / 393 method |
metadata-proven | như trên |
2.2 Quy mô scene — quyết định kiến trúc trung tâm
| Claim | Mức bằng chứng | Nguồn / provenance |
|---|---|---|
Có đúng MỘT scene trong build: Main (serialized file level0) |
asset-census | [report: asset_lane.md] §3 |
| Kích thước sau export: 481 217 168 B (~481 MB), 143 032 object | asset-census | như trên |
Phân bố object: 38 375 GameObject · 24 452 Transform + 13 923 RectTransform · 12 561 CanvasRenderer · 25 Canvas · 7 718 ParticleSystem (+7 718 Renderer) · 6 491 SpriteRenderer · 31 354 MonoBehaviour · 3 894 MonoScript |
asset-census | [report: asset_lane.md] §3, §4a |
Trong đó 30 082 tham chiếu m_Script trỏ tới 190 class khác nhau của Assembly-CSharp (trên tổng 731) |
scene-census | [index: scene_census.json] |
Không có level1..N, không AssetBundle, không Addressables catalog, không scene cộng thêm |
asset-census (bằng chứng phủ định) | [report: asset_lane.md] §3 |
| Toàn bộ game (mọi màn hình, menu, popup, trạng thái gameplay) nằm trong scene đó | asset-census | như trên |
Mọi asset nằm trong một bundle data.unity3d (32 167 543 B) chứa level0, globalgamemanagers.assets (3 932 obj), resources.assets (2 830), sharedassets0.assets (1 014) |
asset-census | [report: asset_lane.md] §2 |
2.3 Chi phí của quyết định đó — đo được
| Claim | Mức bằng chứng | Nguồn / provenance |
|---|---|---|
Import sạch scene 481 MB trong Editor tốn > 30 phút, CPU-bound 98 %, sinh ~24 GB cảnh báo ReadObjectThreaded |
build-measured | G027 trong gap list (nội bộ) |
Một lần import THẤT BẠI: Editor bị SIGKILL (returncode -9) sau 2 544 s (42 phút). Không phải hết bộ nhớ (128 GB, còn trống 93 %, không có jetsam) và không phải timeout của lane |
build-measured | G028 trong gap list (nội bộ) |
| Scene đã giải mã (28 k component có dữ liệu thật) nặng hơn nhiều so với scene toàn dummy trước đó (vốn build được) | build-measured | G028 |
Một build được đặt trong process group của caller đã bị SIGKILL đúng 60 phút ⇒ phải chạy qua spawn_detached.py với os.setsid() |
build-measured | G120; [report: project_lane.md] §6 |
APK dựng lại: 46 124 139 B, 460 entry / 264 921 298 B chưa nén; scene chia thành level0.split0..145 (146 mảnh) |
build-measured | [report: project_lane.md] §7 |
2.4 Hình ảnh & hiệu năng
| Claim | Mức bằng chứng | Nguồn / provenance |
|---|---|---|
URP asset Mobile_RPAsset: m_RenderScale: 0.7 |
scene-YAML | [restore project: Mobile_RPAsset.asset:29] |
Cùng file: m_MSAA: 1 (tắt), m_SupportsHDR: 0, m_MainLightShadowsSupported: 0, m_AdditionalLightShadowsSupported: 0, m_ShadowDistance: 50, m_ShadowCascadeCount: 1, m_UpscalingFilter: 1, m_FsrSharpness: 0.92, m_RequireDepthTexture: 1, m_RequireOpaqueTexture: 0, m_EnableLODCrossFade: 0 |
scene-YAML | như trên |
| Nén texture Android = ASTC: ASTC_6x6 ×78 (gồm cả hai trang atlas 4096×4096), ASTC_8x8 ×9, RGBA32 ×50, Alpha8 ×20 (atlas SDF của TMP), RGB24 ×1 | asset-census | [report: asset_lane.md] §0 |
2 sprite atlas: Cat (125 sprite) và _UI (374 sprite), cả hai 4096×4096 ASTC 6x6 — tên file mã hoá cả định dạng: sactx-0-4096x4096-ASTC 6x6-Cat-ad05b281 |
asset-census | [report: asset_lane.md] §5 |
boot.config: androidStartInFullscreen=1, androidRenderOutsideSafeArea=1, adaptive-performance-{samsung,google}-boost-launch=1 |
package-source | [report: project_lane.md] §7 |
| Audio: 16 clip Vorbis (~4 phút tổng) — xem doc 04 | asset-census | [report: asset_lane.md] §5 |
| Spine 4.1.24 JSON, 13 skeleton + 13 atlas — xem doc 05 | asset-census | [report: asset_lane.md] §0 |
2.5 Bảo vệ khi ship (mục nội bộ, đã lược bỏ)
2.6 Package bên thứ ba
| Assembly đã ship | Package | Nguồn |
|---|---|---|
DOTween, DOTweenPro, DemiLib |
DOTween Pro — Asset Store 32416 (user sở hữu) | quy tắc dự án (nội bộ) R2 |
AllIn1SpriteShaderAssembly |
All In 1 Sprite Shader — 156513 (sở hữu) | như trên |
ParticleImage |
UI Particle Image — 235001 (sở hữu). Version phải khớp binary: SOT có EmitAtWorldPosition/LocalPosition/ScreenPosition (0x484C460/0x484C1D0/0x484C4C4); bản trong project cũ hơn, thiếu chúng (G404) |
như trên; G404 |
ACTk.Runtime |
Anti-Cheat Toolkit — 202695 (sở hữu) | như trên |
MK.Toon, MK.Toon.URP |
MK Toon — 178415 (sở hữu) | như trên |
ShapesRuntime |
Shapes 4.6.0 — 173167 (sở hữu) | [report: project_lane.md] §4 |
OccaSoftware.OutlineObjects.Runtime |
Outline Objects 3.2.0 — 215454 (sở hữu) | như trên |
InfiniteValueAsm |
Infinite Value (BigDecimal) 2.2.0 — 194963 (sở hữu) | như trên |
spine-csharp, spine-unity |
spine-unity 4.1 (miễn phí, esotericsoftware.com) | như trên |
Whinarn.IndMeshSimplifier.Runtime |
UnityMeshSimplifier v3.1.1 (MIT) | như trên |
SlicedFilledImage |
MIT open-source (hiryma / Unity UI Extensions), vendor dưới dạng source trong namespace toàn cục | G128 |
| Claim | Mức bằng chứng | Nguồn |
|---|---|---|
| 8 package thương mại đều là bản có bản quyền của user; không cái nào bị stub hay thay thế (R2) | package-source | [report: project_lane.md] §4 |
| 3 package có rủi ro lệch version, được ghi lại chứ không giấu: Shapes 4.6.0, Outline Objects 3.2.0, Infinite Value 2.2.0 là bản store hiện tại còn SOT ship bản cũ hơn | package-source | G117, G118, G122 |
Kiểm tra layout serialize của InfVal trước khi tin dùng: v2.2.0 vẫn là public partial struct InfVal trong namespace InfiniteValue với s_digits rồi s_exponent đúng thứ tự SOT ship |
package-source | [report: project_lane.md] §4 |
2.7 Hình dạng project sau khôi phục
| Claim | Mức bằng chứng | Nguồn |
|---|---|---|
Export chuẩn: [export AssetRipper: ar-project-decrypted-v1] — 4 724 file / 606 937 082 B, 0 lỗi export, 0 lỗi import |
build-measured | [report: reexport_lane.md] §4 |
Trước giải mã: 3 678 file .cs, 100 % là stub "Dummy class". Sau: 439 .cs thật, 228 mang khai báo field, 1 535 field instance public/[SerializeField] |
build-measured | [report: reexport_lane.md] §5.1 |
Nguyên nhân gốc của đống dummy là HAI thứ, không phải một: (1) metadata mã hoá, và (2) job cũ trỏ vào assets/bin/Data đơn lẻ nên AssetRipper không bao giờ thấy lib/arm64-v8a/libil2cpp.so và không chạy Cpp2IL |
build-measured | [report: reexport_lane.md] §1 |
GUID giữ nguyên giữa export cũ và mới ⇒ 30 082 tham chiếu m_Script trong scene vẫn phân giải, không cần remap |
build-measured | [shared facts nội bộ] |
THÂN METHOD VẪN RỖNG. Cpp2IL dựng lại chữ ký và field, không bao giờ dựng lại logic. Update(), OnEnable(), ShootBullet() đều là { } |
build-measured | G129; [report: reexport_lane.md] §5 |
Compile script: PASS, 0 error CS, 118 assembly trong Library/ScriptAssemblies |
build-measured | [report: project_lane.md] §7 Gate B |
38 assembly Cpp2IL trùng với package thật được chuyển đi (L1-Plugins~/), không xoá |
build-measured | [report: project_lane.md] §2 |
46 assembly SDK R1 được giữ lại và fail-closed do cấu trúc (không có thân method ⇒ mọi lời gọi trả default) |
build-measured | như trên |
unity_fileid.py hiện thực hàm băm của Unity int32_le(MD4("s\0\0\0" + namespace + class)[0:4]), được xác minh bằng dữ liệu của chính project (tái tạo -549186028 cho UniversalRenderPipelineAsset) |
build-measured | [report: project_lane.md] §4b |
Remap m_Script: 14 135 tham chiếu trên 139 file, 0 không phân giải được |
build-measured | như trên |
3. Mô hình hành vi
3.1 Quyết định "một scene" — và hoá đơn của nó
Cái được: - Không có màn hình loading giữa các phần. Mở shop, vào mỏ, đánh boss — tức thì, vì mọi thứ đã ở trong bộ nhớ. - Không cần AssetBundle, Addressables, catalog, CDN, hay logic cache. Toàn bộ một tầng hạ tầng biến mất. - Không có lỗi tham chiếu chéo scene. Mọi singleton sống suốt vòng đời app (xem doc 07 §3.1).
Cái mất (đo được, không phải lý thuyết):
| Chi phí | Số đo |
|---|---|
| Thời gian import trong Editor | > 30 phút, CPU-bound 98 % (G027) |
| Một lần import thất bại hoàn toàn | SIGKILL sau 2 544 s — không phải OOM, không phải timeout (G028) |
| Rác log | ~24 GB cảnh báo ReadObjectThreaded |
| Kích thước file scene | 481 MB trong một file .unity duy nhất |
| Kích thước trong APK | 146 mảnh level0.split0..145 |
| Bộ nhớ lúc chạy | mọi thứ được load, kể cả màn hình chưa từng mở |
| Merge của lập trình viên | một file YAML 481 MB — không thể merge, thực tế là khoá độc quyền |
Và một hệ quả gameplay tinh vi — đây là chỗ kiến trúc rò rỉ vào code:
Vì kẻ địch, đạn và collider luôn tồn tại, không có gì ngăn combat chạy khi người chơi đang ở menu. Thứ duy nhất chặn là một cờ boolean toàn cục
Gamemanager.is_gamingđược kiểm ở từng entry-point. Trong quá trình dựng lại,Enemy.DamagedvàEnemy.Diebị thiếu guard đó (bug G132b) — hậu quả sẽ là quái ăn damage và rơi tiền khi người chơi đang mở shop. Không crash, không lỗi hiện ra: chỉ là kinh tế sai một cách âm thầm. Chi tiết: doc 02 §3.5.
Bài học: kiến trúc một-scene đổi chi phí runtime lấy chi phí quy trình phát triển, và tạo ra một loại bug mới (rò rỉ trạng thái xuyên "màn hình") cần một guard toàn cục có kỷ luật.
3.2 Ngân sách hình ảnh — chọn giảm thứ người chơi không thấy
m_RenderScale: 0.7 # render ở 70% độ phân giải (49% số pixel) rồi upscale
m_UpscalingFilter: 1 # có bộ lọc upscale (FSR), FsrSharpness 0.92
m_MSAA: 1 # TẮT
m_SupportsHDR: 0 # TẮT
m_MainLightShadowsSupported: 0 # TẮT
m_AdditionalLightShadowsSupported: 0 # TẮT
m_ShadowDistance: 50
m_ShadowCascadeCount: 1
m_RequireDepthTexture: 1 # BẬT (có gì đó cần depth)
m_RequireOpaqueTexture: 0 # TẮT
m_EnableLODCrossFade: 0 # TẮT
RenderScale 0.7 là quyết định lớn nhất: nó cắt 51 % số pixel phải tô. Với một game 2.5D
nghệ thuật phẳng, nhìn từ trên, chạy trên màn hình điện thoại mật độ cao, mất mát thị giác gần như
không nhận ra — nhưng lợi ích fill-rate là khổng lồ, đặc biệt với 7 718 ParticleSystem.
Mọi thứ liên quan tới đổ bóng và HDR đều tắt. Nghệ thuật của game là sprite phẳng có viền; nó không
cần bóng động. Bật RequireDepthTexture trong khi tắt RequireOpaqueTexture cho thấy có ít nhất một
hiệu ứng cần độ sâu (viền/outline là ứng viên, xét package Outline Objects).
ASTC 6x6 cho hai trang atlas 4096×4096: đây là điểm ngọt cho sprite mobile — nhỏ hơn ETC2 nhiều
mà chất lượng tốt hơn hẳn ở mức nén tương đương, và mọi thiết bị arm64 hiện đại đều hỗ trợ phần cứng.
Tên file tự mã hoá định dạng (sactx-0-4096x4096-ASTC 6x6-Cat-ad05b281) — chi tiết nhỏ nhưng hữu ích
cho việc kiểm tra sau này.
Chỉ 2 atlas cho toàn game (Cat 125 sprite, _UI 374 sprite) nghĩa là hầu hết UI vẽ trong
rất ít draw call.
3.3 Bảo vệ khi ship (mục nội bộ, đã lược bỏ)
3.4 Hình dạng project sau khôi phục — và giới hạn thật của nó
(khối mã nội bộ đã lược bỏ)
Bài học từ nguyên nhân gốc ([report: reexport_lane.md] §1) đáng nhớ: đống dummy do hai vấn
đề độc lập gây ra, và sửa một cái là không đủ. Kể cả với metadata đã giải mã, việc trỏ AssetRipper
vào assets/bin/Data đơn lẻ vẫn cho ra dummy, vì nó không nhận ra cấu trúc game Android và không bao
giờ chạy Cpp2IL. Cây staging phải là layout Android đầy đủ.
Và ranh giới trung thực, phải nhắc lại:
Cpp2IL dựng lại chữ ký và field, không bao giờ dựng lại logic.
Update(),OnEnable(),ShootBullet()là{ }. APK dựng từ export này là một vỏ cấu trúc: nó cài được, khởi động được, load được scene và asset thật, và không làm gì cả. Nó không phải một game đang chạy hay đã khôi phục.Việc khôi phục logic là một lane riêng, và mọi thân method ở đó là L1 ANALYSIS — dựng lại clean-room từ ARM64 + quan sát runtime, không bao giờ là "source gốc".
Con số của lane đó cho thấy quy mô thật: 22 class đã ship trong tổng 731; trong đó chỉ 5 được xác minh bằng oracle field-delta trên thiết bị.
3.5 Xếp hạng ưu tiên bằng dữ liệu, không bằng cảm giác
Bài học phương pháp luận mạnh nhất của toàn bộ project:
(khối mã nội bộ đã lược bỏ)
Và runtime đảo ngược phỏng đoán tĩnh ở hai chỗ quan trọng:
| Phỏng đoán | Thực tế |
|---|---|
BulletShooter nghe như trái tim của game |
0 lời gọi. Là shooter legacy không dùng. |
| Class nóng nhất là logic gameplay | SlicedFilledImage — component UI vẽ thanh máu — 169 246 lời gọi, gấp 10 lần Bullet (16 321). |
Không có oracle runtime, lane khôi phục sẽ đổ công sức vào
BulletShooter(0 giá trị) và bỏ quaSlicedFilledImage(class quan trọng nhất về hiệu năng). Đo, đừng đoán.
3.6 Bài học phương pháp khôi phục — đợt 3 (mỗi bài học là một lỗi thật đã trả giá)
Đây là phần "method" của bộ tài liệu: cách các sự thật ở doc 01/05/07 được lấy ra, và bẫy nào đã cắn.
Mỗi mục trích ledger gap list (nội bộ).
| Bài học | Chuyện đã xảy ra | Quy tắc rút ra | G-id |
|---|---|---|---|
Cpp2IL bỏ rơi khởi tạo field. Khởi tạo int x = 5; / = new Dictionary() biên dịch vào ..ctor / ..cctor, Cpp2IL emit field trần |
Cat_manager.warpOffsets null ⇒ NRE Warp; Tree_manager.GameTime_Max = 0 ⇒ màn thua ở frame 1, không ném gì; Gamemanager._textCooldowns / _moneyCache null; Shop_manager.Dia_FirstBuy_Used null trên save mới. 39 ctor / 24 class trong overlay có khởi tạo bị bỏ |
Đọc ..ctor (> 48 B) và ..cctor (> 16 B) của MỌI class trước khi dựng bất kỳ method nào — worklist _evidence/Ctors/CTOR_INITIALIZER_WORKLIST.md. Mặc định giá trị sai tệ hơn null: nó im lặng |
G306, G319, G320, G350 |
IDA gộp hàm qua tail call. Method private chỉ được gọi ở vị trí đuôi (B callee) hoặc helper noreturn rơi thẳng vào prologue kế ⇒ không có RET để IDA cắt |
OnPopulateMesh "2 312 B" thật ra là 3 hàm; Tree.Die_Reload "ôm" trọn Tree_manager.Tree_Destroyed ⇒ ship sai class, sai visibility, một stub rỗng, và một null-guard bịa. Quét toàn bộ: 122 / 2 008 method (6.1 %) của 81 class bị gộp vào 87 hàm chủ |
assert ida_funcs.get_func(rva).start_ea == rva trước khi tin bất kỳ kích thước hay thân hàm nào; tách bằng del_func/add_func tại RVA metadata. IDB đã tách lưu riêng (libil2cpp.so.split.i64), bản gốc đóng băng |
G345, G346, G601, G348 |
Tên field <PrivateImplementationDetails> LÀ SHA-256 của blob. Roslyn đặt tên field khởi tạo mảng bằng SHA-256 của chính dữ liệu |
Trước đó mọi bảng RuntimeHelpers.InitializeArray (DiaRewards, degreeRates, HighTier_Gun_Table…) bị ghi "không đọc được" |
Đọc size byte tại offset metadata mà dump.cs in sẵn, SHA256 == tên field ⇒ bằng chứng tuyệt đối (preimage không thể trùng). 448/448 blob toàn build khớp. Consumer + kiểu phần tử lấy từ SZArrayNew(TypeInfo, n) trước InitializeArray |
G350, G353, G702 |
Meta GUID của script là một phần scene. Dời .cs mà không dời .cs.meta ⇒ Unity đúc GUID mới ⇒ component trong scene thành Missing Script ⇒ Awake không chạy ⇒ instance null |
Gun_manager.cs.meta bị đúc lại khi né vòng compile với I2 ⇒ 2 NRE trên thiết bị, bị chẩn đoán nhầm sang codegen ("không có Gun_manager trong scene" — đã rút lại) |
Gate đứng verify_script_guids.py trước mỗi import (220 GUID scene, 0 unresolved); overlay không bao giờ mang .meta; tìm component theo m_Name trước, rồi mới so GUID — "0 lần xuất hiện của GUID X" không là bằng chứng vắng mặt khi chưa chứng minh X là GUID của SOT |
G307, G308, G314, G315 |
Singleton null không thể bị compile oracle bắt. 22 manager có Awake rỗng ⇒ 31 X.instance null trên thiết bị |
4 NRE đầu tiên khi boot chain chạy tới | Guard tĩnh check_singletons.py (mỗi X.instance được dereference phải có instance = this trong class) chạy mỗi build — nó bắt được 1 + 7 dereference mới sinh ra khi viết thân method (WeeklyShop, Setting, StepUp, …) trước khi lên máy. Kèm check_no_meta.py |
G296, G297, G308, G310, G330 |
Cuối hàm = get_func().end_ea, không phải "địa chỉ trông hợp lý". |
Hai lần quét quá đà sang hàm kế và sinh claim sai (InitNewGame "còn 17 call chưa dựng" — đã rút lại, đó là ServerConfig_manager.LoadFromSaveData) |
Lấy end_ea trước khi quét |
G324 (G208 đóng), G221 |
| Khi bộ trích tay và decompiler mâu thuẫn về nguồn thanh ghi, decompiler thắng. | Bộ trích tự động gán khối Mission_* cho Adventure_manager (mapping cũ sống sót qua redefinition); siết chặt chỉ làm nó giấu lỗi (10 hàng → 2 hàng) |
Đổi công cụ (Hex-Rays dump ra file), chứng minh từng hàng, không ship bản đồ suy đoán vào đường nạp save | G328, G329 |
| Một lời gọi "trùng" có thể là cổng an toàn. | EvaluateRoutine gọi ComputeElapsed hai lần; lần 2 sau đồng bộ giờ mới được trả tiền |
Đọc hàm, đừng dọn hàm | G334 |
| Xác minh RVA bằng tên trước khi dùng. | Một Awake gán nhầm class (FacebookManager dán nhãn DailyCheck); 3 "lệch" khác là IDA gộp accessor event với Awake |
idc.get_func_name(rva) cho mọi RVA lấy từ bảng |
G297 |
| Ranh giới R1 đặt ở lời gọi SDK, không ở hàm lắp payload. | AppsFlyerInit.SendLog_Launch lắp payload từ state cục bộ (verbatim); SendEvent mới chạm SDK ⇒ đặt ServiceLocator.Analytics ở đó bảo vệ mọi caller |
Tái hiện yêu cầu của client (kể cả sinh credential khách), không tái hiện câu trả lời của provider | G321, G322, G323 |
R13 — tích luỹ base-100 và factory trả null là lỗi im lặng. Giá trị phần-trăm-gốc-100 (X_Value, X_Accum, *_return() + 100) khi còn là stub return 0 nhân cả tích chiến đấu/kinh tế về 0; factory pool return null làm cả tính năng bất động — không ngoại lệ, compile oracle / guard singleton / guard meta đều mù |
4 ca: Relic.Value_Reload (0 damage), Skin_Value_Reload (0 damage, 0 tốc độ), Rebirth.Damage_return, tổng Fish; Object_Polling_Smart (không viên đạn nào spawn) |
Trước khi ship batch nhân với X / 100, truy X tới setter và xác nhận setter đã dựng; cùng luật cho mọi factory/pool |
G359, G363; quy tắc dự án (nội bộ) R13 |
Unity serialize int[] trong scene YAML thành hex blob nén. Tìm dạng danh sách YAML (- 10) không thấy ⇒ kết luận "không serialize" |
Exp_balance bị coi là "sinh lúc chạy" (G311) trong khi Main.unity:20323823 giữ nó dạng hex ⇒ mâu thuẫn G901; thật ra generator chỉ là fallback khi mảng null |
Kiểm hex blob trước khi tuyên bố "không serialize"; với field serialize, blob ctor/generator không phải giá trị ship | G361, G902 |
Kiểm toán stub phải đọc THÂN hàm và đi theo StartCoroutine. "Method không rỗng" ≠ "method làm đủ việc" |
LoadData.Load là dispatcher 10 dòng (SOT: 2 816 B / 91 call) ⇒ save thật nạp ra 0 tiền/0 stage; DailyCheck.Show_WhenReady iterator return null ⇒ NRE cắt ContinueLoad; cổng lật báo OPEN sai |
Bộ sinh theo dõi callee trực tiếp + đích StartCoroutine/IEnumerator transitive (28 → 41 mục); điều kiện lật = 38/38 field khớp trên save KHÔNG mới |
G355, G359 (G801–G803) |
Xác nhận callee từ lệnh BL, không từ nhãn overlay. |
Nhãn trong file dựng lại có thể lệch với hàm thật mà BL trỏ tới |
Đọc đích BL trong disasm khi ghi "X gọi Y" |
G356 |
| Setter khởi động coroutine thừa hưởng trạng thái stub của coroutine. | Bounce_Position.Bounce_Start được coi là "cosmetic" nhưng Coroutine_NoRepeat là return null và Cat.Attack gọi nó mỗi phát ⇒ ship setter một mình là giết đường bắn |
Kiểm đích StartCoroutine bằng tay cho mọi setter ngoài cổng save-path (cổng chỉ phủ đường nạp) |
G369 (G542) |
| Đường ghi save tắt cho tới khi cây ghi đủ 112/112 — fail-safe, không phải lười. | CreateFromCurrentState 6 116 B / 245 call; 45 field nghịch đảo cơ học, 64 cần đọc riêng; một cây ghi dở dang ghi đè save thật bằng null (12 DateTime, Upgrade_Lv, Rebirth_Upgrade_Lv ⇒ mất prestige) |
Cổng phía ghi riêng (112 field + mọi SaveData_return/GetSaveData = 0 stub) rồi mới bật Save(); nghiệm thu = round-trip diff |
G370 |
| Nhánh save-mới có đường khởi tạo riêng của nó. | Baseline base-100 (Relic/Skin) chỉ được đặt trên nhánh non-null của Load ⇒ save mới bắn 0 sát thương dù save thật đúng |
Với mỗi tích luỹ, chứng minh cả hai đường (nạp và mới) đều đặt nó | G371 (G423) |
| Nghiệm thu "pass" vẫn phải liệt kê residual. | 37/38 khớp nhưng offline không trả, autosave không ghi, một timestamp không restore, HUD thiếu — mỗi cái là một hạng mục mở có tên | Verdict = số khớp + danh sách residual có G-id, không phải "PASS" | G367 |
R14 — accessor add/remove [CompilerGenerated] rỗng = field-like event bị bỏ rơi, không bao giờ là code hợp lệ. |
14 event / 9 file bất động (OnMoneyChanged, OnDiaChanged, OnGainRecorded, OnSynced, OnProductsReady, …): mọi += không làm gì, không listener nào bắn ⇒ HUD không làm mới mà không có ngoại lệ |
Khôi phục thành field-like event; check_events.py trong bộ verify fail trên mọi accessor [CompilerGenerated] rỗng |
G376; R14 |
| Một helper stub có thể là 15 lỗi. | DateParseSafe/SanitizeYear return default ⇒ mọi ngày restore = 0001-01-01 (15 call site / 6 file); nghiệm thu chỉ thấy một triệu chứng (mốc vé Trip) |
Khi một residual là "giá trị mặc định", đếm call site của helper trước khi sửa tại chỗ | G372 |
Thứ tự Start vs nạp save của Unity là một đầu vào. |
Tutorial_manager.Start có thể chạy trước khi Load restore Upgrade_Lv ⇒ save đã nạp bị bắt đầu tutorial nhầm (và có thể is_gaming = false ⇒ đạn vô hiệu) |
Với mọi quyết định trong Start phụ thuộc state save, kiểm xem đường nạp có gọi lại nó (SetCleared → CompleteTutorial) |
G375 |
| Sửa một chỗ có thể làm hỏng nhánh kế. | Sửa G901 (guard Exp_Balance_Reload) đã gộp nhầm hai nhánh Adventure (mới / restore) — lỗi của chính bản dựng lại |
Sau mỗi sửa trên đường nạp, đọc lại CBZ rẽ nhánh quanh nó |
G372 |
| Khi code đã loại trừ hết, nghi phạm là CÀI ĐẶT PROJECT. | G424: probe cho thấy đạn bay, mục tiêu có, is_gaming = true, nhưng không va chạm nào tới Enemy.Damaged ⇒ ma trận va chạm layer (DynamicsManager) và thứ tự thực thi script (MonoManager) của bản restore có thể khác globalgamemanagers SOT; triệu chứng thứ hai (tutorial chạy trước khi Upgrade_Lv restore) chỉ ra cùng nguyên nhân |
So hai asset cài đặt đó với SOT trước khi đổ thêm công vào code; script order là một phần của "1:1" | G381 |
Đường ghi save: Clone vs tham chiếu là câu hỏi về TÍNH ĐÚNG, không phải tối ưu. |
Binary gọi Array.Clone đúng 7 lần cho ~20 field mảng; mảng ghi theo tham chiếu đổi theo state sau khi chụp snapshot ⇒ file có thể chứa trạng thái sau lúc "save" |
Ghim từng lời gọi Clone về field (đã ghim 2/7) trước khi bật Save(); lane ghi riêng |
G379 |
adb uninstall KHÔNG xoá save trên bộ nhớ ngoài. |
Một "bản cài mới" vẫn nạp save cũ ⇒ kết quả fresh-save bị nhiễm | Quy trình: rm -rf files/saves trước mỗi lần chạy fresh (G433) |
G381 |
| Sổ cái oracle phải khớp cả THỜI ĐIỂM, không chỉ con số. | Harness offline khớp 17 chữ số và thời điểm trả (20:19:28.370Z) trùng độc lập với dấu autosave của SOT — bằng chứng elapsed không bị "fit" | Với mọi oracle số, tìm một quan sát độc lập thứ hai (timestamp, counter) cùng khớp | G1005 |
| Cài đặt project có thể bị HỎNG bởi mã hoá của bộ export. | m_LayerCollisionMatrix trong DynamicsManager.asset và Physics2DSettings.asset mang ký tự / ở chữ số hex thứ 7 của cả 32 mục (AssetRipper); Unity vẫn import; hành vi lúc chạy với hex hỏng không chứng minh được |
Đối chiếu mọi asset cài đặt với globalgamemanagers ship (UnityPy, class 55/19), không tin file export; giữ bản .before- + receipt |
G383 |
| Loại trừ phải có bằng chứng phủ định, kể cả khi nghi phạm "trông đúng". | Ma trận va chạm hỏng thật — nhưng sửa xong damaged vẫn = 0 32/32 ⇒ không phải nguyên nhân G424; sửa mà không đo lại sẽ khép nhầm |
Mỗi nghi phạm được đóng bằng một lần đo sau khi sửa, không bằng "đã sửa" | G386 |
| Mô hình round-trip phía GHI tìm ra lỗi phía ĐỌC mà diff nghiệm thu không thấy. | Diff nạp-save chỉ so field mình nghĩ tới (38); mô hình "ghi trung thực rồi so với file" trên 128 field lộ ra Lv_Now, 4 bộ đếm Mission, is_Luxury, vòng ExpGet chưa được đọc — một lần ghi sẽ xoá chúng (StageClear_Value 10 → 0) |
Trước khi bật ghi: mô hình round-trip toàn bộ field theo ngữ nghĩa SOT (126 identity / 2 giải thích) và so với overlay (9 phá huỷ) | G388, G389 |
| Getter inline là stub vô hình. | Sáu getter bị inline (UtcNow, LastTrustedUtc, 2 buff time, 2 FreeCash) trả default — không có thân riêng để cổng "method rỗng" bắt; UtcNow = năm 1 đầu độc SanitizeYear (−543 ⇒ loại) và làm InitNewGame ném trên save mới — nguyên nhân thật của residual (1) |
Cổng phía ghi mở rộng liệt kê getter inline; với mọi default(T) trả về, hỏi "ai tiêu thụ nó như dữ liệu thật" |
G389, G386 |
| Bộ sinh cổng có thể tự lừa. | gen_write_stubs.py cũ nhận Save() (rỗng) là đã dựng |
Cổng mở rộng riêng (_compile_write), và không tin OPEN của bộ cũ |
G388 |
Một .cctor thiếu của lớp đồng hồ = mọi ngày tháng sai + crash save mới. |
Time_manager..cctor (0x2AD3AA8) thiếu ⇒ _mono null, AwaitingCheck sai mặc định, UtcNow năm 1 ⇒ SanitizeYear loại mọi ngày, NewUser_Reload ném |
Lớp "tiện ích nền" (clock, RNG, pool) đọc .cctor đầu tiên, trước cả method; một stub ở đó nhân ra hàng chục triệu chứng |
G391 |
| Bật ghi mà không chạy lại nghiệm thu ĐỌC là mở hai lỗi cùng lúc. | Batch 19: công tắc ghi bật ⇒ autosave ném (List null trong CreateFromCurrentState) và đường đọc hồi quy (không đọc được save mà batch 15-18 đọc được) — hai lỗi từ cùng một lô sửa SaveSystem.cs |
Mỗi thay đổi ở file dùng chung đọc/ghi phải qua cả hai nghiệm thu; giữ build "ghi tắt" (batch 18) làm đối chứng | G394 |
| Xếp roadmap bằng quét stub có xếp hạng, không bằng cảm giác. | 1 098 thân rỗng/return trong 49 file (bị thổi bởi iterator Dispose/Reset + false positive Cat) ⇒ Tier 0 (bộ biến đổi tiền tệ Gamemanager: Get_Dia rỗng = dia đóng băng; Power_*_return = 0, R13) → Tier 1 (Tree_manager 12: mọi nút vào chế độ câm; UI_manager 8: mọi cảnh báo câm; SpeedRun 43, Monster 49, Boss 42, Gun 38, Raid 39 + Adventure 42) → Tier 2 (Mission 61, Pet 31, Fish_Tank 31, Buff 10, Relic 13, Polling 7, Setting 24) → Tier 3 liveops (Update 17, Notification 13, StepUp 15, DailyGun 10, WeeklyShop 9, Review 7, DiaPig 6); loại R1: Shop 95 (trừ 3 getter hiển thị), AppsFlyer 44, OfferWall 40, FreeCash 36, ServerConfig 27, Rank 27. Lane chế độ chạy song song: M1 = Monster + Boss, M2 = SpeedRun + Raid |
Đếm stub theo class và hậu quả người chơi thấy (dia đóng băng, nút câm, cảnh báo câm) rồi mới xếp; tách lane theo file sở hữu | G393 |
| Dừng vì giới hạn phiên: ghi thứ tự tiếp tục vào ledger (nội bộ). | 05:30 +07 phiên bị 429 giữa chừng ở 5 lane. Thứ tự sau reset: (1) hồi quy đọc của lane ghi (chặn nghiệm thu), (2) trace runtime G424 + key tiền null, (3) build 20 + round-trip, (4) M1/M2 từ brief, (5) docs | Trạng thái đĩa + thứ tự tiếp tục là một dòng ledger, không nằm trong đầu ai | G396 |
| Một hằng số vector sai MỘT thành phần qua được mọi guard. | CamForward thiếu pitch 40° (y = 0 thay vì −0.6428) ⇒ chia cho 0 ⇒ mọi viên đạn ở ±∞ — không ngoại lệ, không cảnh báo; compile/singleton/meta/event guard đều mù; 7 vòng loại trừ (ma trận va chạm, thứ tự script, ctor, Damaged, rb.velocity, xuyên hầm, layer) đều đúng mà vẫn sai |
Xác minh hằng vector từng thành phần đối chiếu với binary (Internal_FromEulerRad nạp S0/S1 từ .rodata), không đọc "Euler(…)" theo cảm giác; với mọi phép chia, hỏi mẫu số có thể là 0 do hằng sai không |
G400 |
| "Hồi quy" có thể là quy trình test. | "Không đọc được save" ở batch 19: adb push trên cài mới tạo files/saves/ dưới user shell trước khi app tạo ⇒ app không đọc được; code đọc byte-identical với batch 18 |
Diff file thay đổi trước khi kết luận hồi quy; quy trình: cài → chạy 1 lần → force-stop → push → ls -la + md5 → chạy lại |
G399 |
| Một ngoại lệ trước continuation nuốt cả nửa sau của việc nạp. | EvaluateAndApply ném ⇒ ContinueLoad không chạy ⇒ 8 field ngày còn năm 1, trông như "lỗi restore ngày" |
Khi nhiều field cùng ở giá trị mặc định, tìm điểm ném sớm nhất trên đường nạp trước khi sửa từng field | G400 |
start_ea == RVA là chưa đủ — kiểm cả BIÊN CUỐI. |
Lane M2 (G1306): Game_Sweep 0x2B4152C đã nuốt SpeedRun_Start_UI.Reload 0x2B41708; Raid Gun_SelectPanel_Open 0x2AFD44C nuốt Raid_GunSelect_Panel.Panel_Open 0x2AFD468 — IDB đã tách (G348) vẫn thiếu hai cắt này |
Thêm kiểm end_ea ≤ RVA kế tiếp trong script.json vào công thức chuẩn; sinh lại .split.i64 với hai cắt |
G406 |
| R2: kiểm VERSION package bên thứ ba theo bề mặt API của binary, không chỉ sự có mặt. | Dia_Attraction.Dia_Spawn gọi ParticleImage.EmitAtWorldPosition — bản package trong project không có method đó ⇒ compile đỏ toàn lane; SOT ship một release mới hơn |
Với mỗi package: liệt kê method public mà Assembly-CSharp gọi (từ dump.cs/xref) và so với bản đã tải; tải đúng version (owned), không vá code package |
G404 |
| Sample/Resources của package stock có thể CHE asset của SOT. | Import TMP Essential Resources + I2 Examples + Shapes Resources tạo 23 đường Resources.Load trùng; "TMP Settings" resolve không tất định giữa stock và SOT ⇒ font mặc định / sprite asset / style sheet / bảng ngắt dòng / kern / quy tắc Hangul có thể lệch âm thầm (= ứng viên của glyph lỗi ở capsule) — dù 18 font asset, 5 sprite asset, 27 material đều giống hệt SOT (GUID, atlas, glyph, metrics, fallback) |
Sau mỗi lần import package: liệt kê collision Resources.Load; xoá stock/sample (giữ shader + LiberationSans mà 27 material tham chiếu); snapshot + verify_script_guids.py + ImportGate + receipt |
G407 |
| Guard singleton phải bỏ qua comment. | Comment văn xuôi "SOT: Admob.instance.ShowRewardAd" của M2 làm check_singletons.py đỏ cho mọi lane |
Strip /* */ và // trước khi quét (cả phía dùng lẫn phía gán; gán trong comment không còn thoả) — lane chế độ được ghi hành vi SOT trong comment thoải mái |
G402 |
| Che khuất Resources đã gỡ — receipt (G411). | Snapshot 113 file; xoá Assets/TextMesh Pro/Resources/ (9), Assets/Shapes/Resources/ (3), Assets/I2/Localization/Examples/ (36); giữ TMP Shaders + LiberationSans; đường Resources.Load trùng 23 → 3 (3 còn lại là của chính SOT: references/sprites/l_{en,es,fr}); "TMP Settings" resolve duy nhất; GUID gate 220/0/0, ImportGate 0 lỗi |
Sau khi gỡ: đếm lại collision và chứng minh phần còn lại là của SOT | G411 |
| Package vendor có thể bị NHÀ PHÁT TRIỂN game sửa (R2 ranh giới). | ParticleImage.EmitAtWorldPosition/LocalPosition/ScreenPosition có trong binary nhưng không có ở bất kỳ release store nào (1.2.2 mới nhất, 0/91 asset) — tiền đề G404 "bản cũ" sai; đây là code do game thêm vào class vendor; class public sealed, asmdef riêng, không entry point ⇒ extension method bất khả |
Quyết định (G413): sửa một token sealed class → sealed partial class dưới banner provenance (snapshot trước) + một file partial do SOT tác giả ParticleImage.SotAdditions.cs với 3 thân dựng từ binary, header "developer modification … reconstructed — not vendor code"; feel lane viết vào [tài liệu nội bộ: _packages], project lane áp + ImportGate + sinh lại DLL oracle. Không bao giờ stub assembly |
G411, G413 |
| Một tích base-100 áp thô là lỗi ×100 im lặng. | Batch 20: tiền ≈ ×300 SOT từ cùng file — không ngoại lệ, chỉ số to bất thường | Với mọi mắt X/100, log giá trị trước và sau chia trên cùng frame và so với .sot trước khi tin đường kinh tế |
G414 |
| Nửa sau nạp không chạy = danh tính rỗng trong bộ nhớ = nguy cơ đúc lại. | ContinueLoad bị null chặn ⇒ bộ ba PlayFab rỗng ⇒ Login đi nhánh đăng ký mới (G506); chưa ghi đè đĩa chỉ vì lần ghi phát lại dữ liệu đã nạp |
Mọi nghiệm thu save phải kiểm bộ ba danh tính trong bộ nhớ, không chỉ trên đĩa | G414 |
Quyết định người dùng 2026-09-05 (nội bộ, G398): ngoại lệ R4 cho một phiên GUI Editor để chạy generator ScriptLocalization của I2 — chỉ là fallback nếu dựng từ binary (G397) không khớp; nếu dùng: không lane nào giữ project, đóng Editor ngay, receipt vào ledger. Tiếng Anh trước (build học boot tiếng Anh — dán nhãn non-SOT chỗ khác với chọn theo ngôn ngữ máy); ưu tiên game feel + hệ thống hơn độ rộng bản địa hoá |
Orchestrator giữ thứ tự ưu tiên | G398 | |
| Hệ TEXT là một hạng mục khôi phục riêng, đang làm (không phải claim). | Chỉ thị người dùng: text hiệu ứng (sát thương/tiền), nhãn nút HUD dưới, icon sự kiện phải đúng cỡ. Kế hoạch: (1) ScriptLocalization.cs là đầu ra công cụ ⇒ dựng từ binary như class thường (mỗi getter = một literal GetTranslation("Category/Term"), đối chiếu 317 term với I2Languages.asset) — hết kỷ nguyên text null; (2) glyph lỗi ở capsule tiền/dia = font asset TMP (atlas/material/fallback) ⇒ diff byte với export; (3) cỡ chữ TMP / margin / I2 Localize là scene-serialized ⇒ kiểm không đổi; sửa stub DamageTextSprite*; (4) icon sự kiện = binding SpriteAtlas + nửa hiển thị của Hud_Reload (không gọi service) |
Ghi là ĐANG LÀM trong doc 08 §5 / doc 11 §10; không tuyên bố cơ chế nào cho tới khi lane TEXT trả kết quả | G397 |
4. Framework takeaway
-
Cấu trúc scene là quyết định kiến trúc đắt nhất và khó đảo ngược nhất. Một scene = không loading, không hạ tầng bundle, nhưng: 481 MB, > 30 phút import, không merge được, và trạng thái rò rỉ xuyên màn hình. Framework nên hỗ trợ mô hình lai — một scene lõi bền + các màn hình phụ tải thêm — và đo chi phí import ngay từ đầu, không phải khi đã 481 MB.
-
Nếu chọn một-scene, guard trạng thái toàn cục phải là hạ tầng bắt buộc.
is_gamingkhông được là một dòngifmà mỗi người phải nhớ chép. Nó phải là một cổng ở tầng framework (base class, hoặcIDamageablegate). Bằng chứng: bug G132b. -
Ngân sách hình ảnh: giảm thứ người chơi không nhìn thấy, giữ thứ họ thấy.
RenderScale 0.7(−51 % pixel) + tắt toàn bộ bóng/HDR/MSAA + 2 atlas ASTC 6x6. Đây là một cấu hình mobile đã ship, đáng dùng làm mặc định cho game 2D/2.5D. -
Ít atlas lớn, định dạng mã hoá trong tên. 2 atlas × 4096² phục vụ 499 sprite.
-
Bảo vệ là quản trị rủi ro, không phải bức tường — và mỗi lớp chỉ chặn một loại tấn công. ACTk (sửa bộ nhớ). Save plain-text là một lỗ trống có chủ đích — hợp lý cho idle single-player, đáng chất vấn khi có bảng xếp hạng. Hãy nêu rõ mô hình mối đe doạ của từng lớp; đừng gộp thành "game này có bảo vệ". Mã hoá 55 % metadata + đổi tên export chặn được mọi công cụ tự động — đó là giá trị thật. Nó không ngăn được người quyết tâm, vì key phải nằm trong binary. Hãy hiểu rõ mình mua gì. Và đừng trộn hai mối đe doạ: chống dịch ngược (metadata) và chống phân phối lại (license) là hai bài toán khác nhau, hai lớp khác nhau.
-
Khi hook, can thiệp vào quyết định, không vào hậu quả.
-
Xếp hạng công việc bằng dữ liệu quan sát, không bằng tên. Điều tra scene + topology manager (tĩnh) → thu hẹp; trace runtime (động) → xếp hạng. Runtime đảo ngược phỏng đoán ở những chỗ quan trọng nhất.
-
Mua package thật; ghi lệch version, đừng giấu. 8 package thương mại, không cái nào bị stub. 3 cái có rủi ro lệch version và được ghi thành gap (G117/G118/G122). Với package serialize vào scene (như InfVal), kiểm layout field trước khi tin dùng — một field bị đổi tên sẽ âm thầm xoá sạch dữ liệu kinh tế.
-
Trung thực về việc "khôi phục" nghĩa là gì. Chữ ký + field ≠ hành vi. Một APK build được, cài được, chạy được và không làm gì là một vỏ cấu trúc, không phải một game đã khôi phục. Hãy nói đúng như vậy.
-
Cây staging phải phản ánh layout thật của nền tảng.
assets/bin/Datavàlib/arm64-v8a/phải đi cùng nhau, nếu không công cụ sẽ không nhận ra cấu trúc game và âm thầm bỏ qua bước quan trọng nhất.
5. Negative evidence & UNKNOWN
| Mục | Trạng thái | Bằng chứng khoá được nó |
|---|---|---|
| Managed stripping level, API compatibility level, cấu hình compiler IL2CPP, scripting defines, thứ tự graphics API | KHÔNG khôi phục được từ evidence | G107 / G131 (một phần đã đóng nhờ ScriptableObject của URP) |
| Phân bố 25 Canvas theo màn hình | UNKNOWN — chỉ biết tổng | Phân tích scene YAML |
| Phân bố 7 718 ParticleSystem (gameplay vs UI) | UNKNOWN — chỉ biết tổng | như trên |
| Hai GUID script đông nhất trong scene (7 991 và 4 957 instance) | UNKNOWN — không ánh xạ được sang class nào của Assembly-CSharp; chúng nằm trong DLL plugin. Không đoán. |
Mở rộng l1_script_identity.json sang các assembly plugin |
| Gate build Android trên base đã giải mã | CHƯA CHẠY — import asset vẫn đang chạy khi báo cáo được viết; một lần import đã thất bại (G028) | Chạy lại import + build |
| Đo hiệu năng trên thiết bị (fps, thermal, memory) | CHƯA ĐO | Cần một phiên chạy device riêng biệt |
32 shader stub DummyShaderTextExporter |
ĐÓNG MỘT PHẦN (G366): render state của 3 shader sprite đọc từ Shader.m_ParsedForm trong bundle đã giải mã — Sprites/Fill Amount Batched (1 931 material) + Custom/MonsterBounce (240): Blend One OneMinusSrcAlpha (premultiplied), tag CanUseSpriteAtlas / IgnoreProjector / PreviewType=Plane / Queue=Transparent / RenderType=Transparent; Custom/DiamondClipSprite (6): Blend SrcAlpha OneMinusSrcAlpha; cả ba ZTest LEqual, ZWrite Off, Cull Off, Lighting Off, Fog Off, ColorMask RGBA, không stencil, không Fallback, không LOD (LOD 200 của stub là do AssetRipper); _Color kiểu Color. KHÔNG PHẢI SOT: dòng c.rgb *= c.a (premultiply, bắt buộc bởi blend đã đọc, dán nhãn inline). CÒN MỞ: thân hiệu ứng vẫn là placeholder — fill clip (_Reverse/_Vertical), bounce đỉnh (_AnimSpeed/_ScaleAmount/_TimeOffset), diamond clip; GLSL GLES3 đã giải nén từ blob, đang port sang HLSL (dán nhãn reconstruction). 9 ParticleSystemRenderer + 3 MeshRenderer material null — G422 ĐÃ ĐÓNG (G381/G429): slot 0 null của Z Degree FX là hành vi SOT; Hp_Slider có material Shapes thật (Disc Transparent) với m_Enabled 0 ở cả hai scene; import scene không làm rơi gì |
G366, G371, G381 |
Cat.spriteatlas / _UI.spriteatlas (asset .spriteatlas) |
GAP CHỈ VỀ ĐỊNH DẠNG (G013) — bảng đóng gói có trong ar-primary-v1/.../SpriteAtlas/{Cat,_UI}.json (125 + 374 entry) |
Dựng lại .spriteatlas từ JSON |
Sprite Hand |
G014 — 1 sprite (sharedassets0 pathID 803) vắng trong project export | Trích từ corpus UnityPy |
Hệ text (G397 → G407) — ĐANG LÀM: font: 18 font asset, 5 sprite asset, 27 material TMP giống hệt SOT (GUID, atlas, glyph, metrics, fallback; 4 bitmap font 0 byte byte-identical) ⇒ không phải lỗi font; lỗi tìm ra = tài nguyên stock/sample che asset SOT ở 23 đường Resources.Load — ĐÃ GỠ (G411): 23 → 3 (3 còn lại là của SOT), "TMP Settings" resolve duy nhất, ImportGate xanh. ScriptLocalization (317 term) và cỡ text hiệu ứng vẫn đang làm |
MỞ — che khuất đã gỡ; ScriptLocalization + cỡ chữ đang làm | Receipt sau khi xoá; dựng ScriptLocalization từ literal GetTranslation |
| Không quan sát thấy | Không có level thứ hai, AssetBundle, Addressables, hay CDN asset ở bất kỳ đâu trong build | [report: asset_lane.md] §3 |
[G012 — DIỄN GIẢI LẠI] 4 font Damage, Damage (Cri), Dia, Money_Cri có m_FontData độ dài 0 KHÔNG PHẢI TTF HỎNG — chúng là BITMAP FONT: bảng 42 ký tự (+ . 0-9 B K M T a-z) + atlas PNG. Bảng + PNG CHÍNH LÀ toàn bộ font. Phải giữ nguyên dạng bảng+atlas; thay bằng một TTF sẽ âm thầm đổi cả hình dạng chữ (hoa/thường) lẫn metrics. Đây là một hạng mục nội dung thật cho hoạ sĩ: 4 atlas bitmap font, 42 glyph mỗi cái. [SOT: DevXProject] từng ghi 4 file .ttf 0 byte — đó là file bịa. |
[evidence codegen: UPPERCASE_FINDING.md]; G012 REINTERPRETED; bảng ký tự đã kiểm độc lập trong Assets/Font/Damage.asset |
|
| Không quan sát thấy | libil2cpp.so không bị pack (section ELF nguyên vẹn) — chỉ metadata bị mã hoá và export bị đổi tên |
[shared facts nội bộ] |
6. Raw evidence
[shared facts nội bộ]— danh tính, hash, cipher metadata (mục "CORRECTED FINDING"), kiểm kê code[report: asset_lane.md]— thống kê scene, kiểm kê asset, định dạng texture, audio, Spine[report: reexport_lane.md]— nguyên nhân gốc dummy, thay thế metadata, so sánh trước/sau[report: project_lane.md]— pin Editor, manifest package, PlayerSettings, kết quả gate[report: codegen_lane.md]— xếp hạng ưu tiên, oracle runtime, trạng thái khôi phục[restore project: Mobile_RPAsset.asset]— cài đặt URP (m_RenderScale: 0.7)[index: scene_census.json], priority_static.json}— điều tra scene + xếp hạng[runtime trace: runtime_ranking.json]— 505 method đã chạy[tài liệu nội bộ: check_singletons.py], check_no_meta.py},[restore project: verify_script_guids.py]— ba gate đứng sinh ra từ §3.6[native: libil2cpp.so.split.i64](IDB đã tách 87 hàm gộp) ·libil2cpp.so.pristine.i64(bản gốc đóng băng, sha256e8273f34…)- Gaps: G013, G014, G027, G028, G101, G107, G117, G118, G120, G122, G129, G131, G132b; đợt 3: G296–G297, G306–G308, G314–G315, G319–G324, G328–G329, G334, G345–G348, G350, G353, G601, G702 trong
gap list (nội bộ)