Files
vphone-cli/VPhoneExecutable/VPhoneCommand/VPhonePatchKit/Patch/PatchRecord.swift
T
LakrandClaude Opus 5.5 ae5e4531a5 Name every patch {component}-{effect}-{name}
The 117 bundled patch identifiers had grown five naming schemes
(kernel.x, jb.x, kernelcache_jb.x, txm_dev.x, bare names). Each one is now
{component}-{effect}-{name}:

- component: avpbooter, ibss, ibec, llb, txm, kernel, devicetree, dyld,
  preboot, or system-<binary> for a guest binary or file.
- effect: boot when the patch is boot-essential, exp when the standard
  preset leaves it off, cfw otherwise. A catalog test enforces this.
- name: snake_case, no hyphen, so the identifier splits from the right.

Record sites are now always <identifier>.<site>. The underscore-prefix
rule in covers(recordIdentifier:) and in the gate is gone: the new names
contain underscores, so kernel-boot-post_validation would otherwise have
covered kernel-boot-post_validation_unsigned. The 25 records that relied
on it (amfi_trustcache_1, launch_constraints_mov, sandbox_ext_N, ...) now
use a dot.

Old identifiers are not migrated. A VM whose PatchPlan or PatchSelection
names one must be patched again. The bundle becomes 2.2.0 and Launchpad
requires 2.2.0, so it never meets an old identifier from a bundle.

Launchpad's patch table shows Component, Effect and Name columns in place
of Identifier and Patch Set; the set moves to the detail line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 15:20:20 +09:00

77 lines
2.3 KiB
Swift

// PatchRecord.swift — Per-patch verification record for migration validation.
import Foundation
/// A single patch application record, used to compare Python vs Swift output.
public struct PatchRecord: Codable, Equatable, Sendable {
/// Unique patch identifier (e.g., "kernel-boot-bsd_init_rootvp").
public let patchID: String
/// Component being patched (e.g., "kernelcache", "ibss", "txm").
public let component: String
/// File offset where the patch is applied.
public let fileOffset: Int
/// Virtual address (if applicable, nil for raw binaries).
public let virtualAddress: UInt64?
/// Original bytes before patching.
public let originalBytes: Data
/// Replacement bytes after patching.
public let patchedBytes: Data
/// Capstone disassembly of original bytes.
public let beforeDisasm: String
/// Capstone disassembly of patched bytes.
public let afterDisasm: String
/// Human-readable description of what this patch does.
public let patchDescription: String
public init(
patchID: String,
component: String,
fileOffset: Int,
virtualAddress: UInt64? = nil,
originalBytes: Data,
patchedBytes: Data,
beforeDisasm: String = "",
afterDisasm: String = "",
description: String,
) {
self.patchID = patchID
self.component = component
self.fileOffset = fileOffset
self.virtualAddress = virtualAddress
self.originalBytes = originalBytes
self.patchedBytes = patchedBytes
self.beforeDisasm = beforeDisasm
self.afterDisasm = afterDisasm
patchDescription = description
}
}
extension PatchRecord: CustomStringConvertible {
public var description: String {
let addr = virtualAddress.map { String(format: " (VA 0x%llX)", $0) } ?? ""
return String(
format: " 0x%06X%@: %@ → %@ [%@]",
fileOffset,
addr,
beforeDisasm.isEmpty ? originalBytes.hex : beforeDisasm,
afterDisasm.isEmpty ? patchedBytes.hex : afterDisasm,
patchID,
)
}
}
public extension Data {
/// Lowercase hex, the spelling every patch log and record uses.
var hex: String {
map { String(format: "%02x", $0) }.joined()
}
}