mirror of
https://github.com/freemkv/freemkv-unlock.git
synced 2026-10-07 18:13:11 +00:00
Compare commits
4
Commits
v1.7.0-rc7
...
v1.7.1-rc2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
422147f6ad | ||
|
|
72d5b8fb3e | ||
|
|
407c7f59e3 | ||
|
|
199a3ffee6 |
@@ -10,7 +10,7 @@ jobs:
|
||||
identity:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Reject banned author/committer emails
|
||||
|
||||
+4
-4
@@ -2,6 +2,10 @@
|
||||
|
||||
## [1.7.0] — 2026-09-02
|
||||
|
||||
### Added
|
||||
|
||||
- Preliminary, beta unlocking support for **Renesas** optical drives and for drives running **freemkv firmware**. Both are early and unvalidated; the established LibreDrive / AACS / CSS unlock routes are unchanged.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `ld/profile`: `find_by_drive_id` no longer mis-binds a drive whose reported `product_id` is real but not catalogued to a sibling model's profile. The product-id-blind loose match is now gated on an empty product id, so a wrong profile — and its wrong LibreDrive microcode — can't be selected for a drive that does report a product id.
|
||||
@@ -10,10 +14,6 @@
|
||||
|
||||
- Version aligned to 1.7.0 for the unified release.
|
||||
|
||||
### Docs
|
||||
|
||||
- Added a third-party NOTICE clarifying that the LibreDrive microcode bundled in `src/ld/profiles.json` is proprietary MakeMKV material, not covered by this repository's MIT license.
|
||||
|
||||
## [1.6.14] — 2026-08-31
|
||||
|
||||
### Changed
|
||||
|
||||
Generated
+1
-1
@@ -142,7 +142,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "freemkv-unlock"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"base64",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "freemkv-unlock"
|
||||
version = "1.7.0"
|
||||
version = "1.7.1"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
license = "MIT"
|
||||
|
||||
+65
-18
@@ -307,15 +307,14 @@ fn load_from_str(data: &str) -> Result<Profiles> {
|
||||
serde_json::from_str(data).map_err(|_| Error::ProfileParse)
|
||||
}
|
||||
|
||||
/// Find a profile matching a drive's INQUIRY fields.
|
||||
/// Find a profile matching a drive's INQUIRY fields, whitespace-trimmed.
|
||||
///
|
||||
/// Per platform section (MT1959-A, then MT1959-B, then Renesas). A drive that
|
||||
/// reports a `product_id` matches ONLY on the full identity including it — if
|
||||
/// no cataloged profile matches exactly, the drive is treated as uncataloged
|
||||
/// (no match), never bound to a same-vendor/revision sibling. The looser
|
||||
/// four-field pass (vendor/revision/vendor_specific/firmware_date) runs only
|
||||
/// when the drive reports no `product_id` at all. All comparisons are
|
||||
/// whitespace-trimmed. Returns the first section that matches.
|
||||
/// Per platform section (MT1959-A, -B, Renesas): (1) exact match including
|
||||
/// `product_id` when the drive reports one; (2) product-id-blind fallback on
|
||||
/// the four-field identity (vendor/revision/vendor_specific/firmware_date).
|
||||
/// Catalogs store a GENERIC product_id (e.g. "BD-RE") vs a drive's specific one
|
||||
/// ("BD-RE BU40N"), so pass 1 misses; pass 2 binds a UNIQUE four-field match —
|
||||
/// a shared four-tuple binds only when the drive reported no product_id.
|
||||
pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Option<ProfileMatch> {
|
||||
let v = drive_id.vendor_id.trim();
|
||||
let prod = drive_id.product_id.trim();
|
||||
@@ -343,21 +342,38 @@ pub fn find_by_drive_id(profiles: &Profiles, drive_id: &crate::DriveId) -> Optio
|
||||
});
|
||||
}
|
||||
|
||||
// The product_id-blind pass runs ONLY when the drive reports none: a
|
||||
// drive that reports a product_id but misses the exact match is an
|
||||
// uncataloged variant, else it'd bind to a sibling's wrong firmware.
|
||||
if prod.is_empty()
|
||||
&& let Some(p) = list.iter().find(|p| {
|
||||
// Product-id-blind fallback: catalogs store a GENERIC product_id while
|
||||
// drives report a specific one, so the exact pass misses. Match the
|
||||
// four-field identity instead; guard mis-binding on shared tuples below.
|
||||
let four_field: Vec<&DriveProfile> = list
|
||||
.iter()
|
||||
.filter(|p| {
|
||||
p.identity.vendor_id.trim() == v
|
||||
&& p.identity.product_revision.trim() == r
|
||||
&& p.identity.vendor_specific.trim() == vs
|
||||
&& p.identity.firmware_date.trim() == date
|
||||
})
|
||||
{
|
||||
return Some(ProfileMatch {
|
||||
profile: p.clone(),
|
||||
platform,
|
||||
});
|
||||
.collect();
|
||||
match four_field.as_slice() {
|
||||
// Exactly one four-field match — unambiguous; bind regardless of
|
||||
// the reported product_id (the common real-drive case, and the
|
||||
// pre-1.7.0 behaviour a UHD-capable LG BU40N relied on).
|
||||
[only] => {
|
||||
return Some(ProfileMatch {
|
||||
profile: (*only).clone(),
|
||||
platform,
|
||||
});
|
||||
}
|
||||
// Shared four-tuple: bind only when the drive reported NO
|
||||
// product_id. A non-empty one that missed the exact pass is an
|
||||
// uncataloged variant and must not mis-bind to a sibling.
|
||||
[first, ..] if prod.is_empty() => {
|
||||
return Some(ProfileMatch {
|
||||
profile: (*first).clone(),
|
||||
platform,
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -427,6 +443,37 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
/// REGRESSION (1.7.0): a real drive reports a SPECIFIC product_id (e.g.
|
||||
/// "BD-RE BU40N") while the catalog stores a GENERIC one ("BD-RE"), so the
|
||||
/// exact pass misses. When the four-field identity is UNIQUE it must still
|
||||
/// bind — the pre-1.7.0 behaviour a UHD LG BU40N relies on for LibreDrive
|
||||
/// unlock. 1.7.0 gated the four-field pass behind an empty product_id, so it
|
||||
/// silently stopped matching and the firmware unlocker was skipped entirely.
|
||||
#[test]
|
||||
fn find_by_drive_id_specific_product_id_binds_unique_four_field() {
|
||||
use serde_json::json;
|
||||
let profiles: Profiles = serde_json::from_str(
|
||||
&json!({
|
||||
"mt1959_a": [
|
||||
{"identity": {"vendor_id":"HL-DT-ST","product_id":"BD-RE",
|
||||
"product_revision":"1.03","vendor_specific":"NM00000",
|
||||
"firmware_date":"211810241934"},
|
||||
"signature":"12345678","firmware":""}
|
||||
]
|
||||
})
|
||||
.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Drive reports the specific marketing product_id; catalog has "BD-RE".
|
||||
let mut id = make_drive_id("HL-DT-ST", "1.03", "NM00000", "211810241934");
|
||||
id.product_id = "BD-RE BU40N".to_string();
|
||||
let m = find_by_drive_id(&profiles, &id)
|
||||
.expect("unique four-field identity must match despite the product_id mismatch");
|
||||
assert_eq!(m.profile.signature, [0x12, 0x34, 0x56, 0x78]);
|
||||
assert_eq!(m.platform, Platform::Mt1959A);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_find_known_drive() {
|
||||
let profiles = load_bundled().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user