mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-18 03:35:56 +02:00
Fix Switch OTA parser for GitHub releases/latest JSON.
Scan asset objects by brace bounds instead of an 800-byte window so browser_download_url survives fat uploader blocks; mirror logic in Lua and add realistic API fixtures.
This commit is contained in:
@@ -48,6 +48,56 @@ int main(void) {
|
||||
"missing ota asset");
|
||||
expect(strcmp(bad.reason, "missing_ota_asset") == 0, "missing_ota_asset reason");
|
||||
|
||||
/* GitHub releases/latest shape: release-level name + fat uploader before browser_download_url. */
|
||||
const char *github_json =
|
||||
"{"
|
||||
"\"tag_name\":\"v0.1.70\","
|
||||
"\"name\":\"0.1.70\","
|
||||
"\"assets\":["
|
||||
"{"
|
||||
"\"url\":\"https://api.github.com/repos/bryanthaboi/gen1recomp/releases/assets/502823880\","
|
||||
"\"id\":502823880,"
|
||||
"\"name\":\"gen1recomp-0.1.70-switch.zip\","
|
||||
"\"label\":\"\","
|
||||
"\"uploader\":{"
|
||||
"\"login\":\"github-actions[bot]\","
|
||||
"\"id\":41898282,"
|
||||
"\"node_id\":\"MDM6Qm90NDE4OTgyODI=\","
|
||||
"\"avatar_url\":\"https://avatars.githubusercontent.com/in/15368?v=4\","
|
||||
"\"gravatar_id\":\"\","
|
||||
"\"url\":\"https://api.github.com/users/github-actions%5Bbot%5D\","
|
||||
"\"html_url\":\"https://github.com/apps/github-actions\","
|
||||
"\"followers_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/followers\","
|
||||
"\"following_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}\","
|
||||
"\"gists_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}\","
|
||||
"\"starred_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}\","
|
||||
"\"subscriptions_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/subscriptions\","
|
||||
"\"organizations_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/orgs\","
|
||||
"\"repos_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/repos\","
|
||||
"\"events_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}\","
|
||||
"\"received_events_url\":\"https://api.github.com/users/github-actions%5Bbot%5D/received_events\","
|
||||
"\"type\":\"Bot\","
|
||||
"\"user_view_type\":\"public\","
|
||||
"\"site_admin\":false"
|
||||
"},"
|
||||
"\"content_type\":\"application/zip\","
|
||||
"\"state\":\"uploaded\","
|
||||
"\"size\":9000573,"
|
||||
"\"browser_download_url\":\"https://github.com/bryanthaboi/gen1recomp/releases/download/v0.1.70/gen1recomp-0.1.70-switch.zip\""
|
||||
"}"
|
||||
"]"
|
||||
"}";
|
||||
ota_release_t gh;
|
||||
expect(ota_parse_release(github_json, &gh) == 1, "parse github-shaped release");
|
||||
expect(strcmp(gh.version, "0.1.70") == 0, "github release version");
|
||||
expect(strcmp(gh.asset_name, "gen1recomp-0.1.70-switch.zip") == 0, "github release asset");
|
||||
expect(strcmp(gh.download_url,
|
||||
"https://github.com/bryanthaboi/gen1recomp/releases/download/v0.1.70/gen1recomp-0.1.70-switch.zip") ==
|
||||
0,
|
||||
"github release download url");
|
||||
ota_decide_update("0.1.69", &gh, &d);
|
||||
expect(strcmp(d.status, "available") == 0, "github release decide 0.1.69->0.1.70");
|
||||
|
||||
const char *sums = "abc123 gen1recomp-1.5.0-switch.zip\n";
|
||||
ota_verify_t v;
|
||||
ota_verify_sha256("gen1recomp-1.5.0-switch.zip", "abc123", sums, &v);
|
||||
|
||||
@@ -95,6 +95,38 @@ static const char *find_json_string(const char *json, const char *key, char *out
|
||||
return out;
|
||||
}
|
||||
|
||||
/* Opening { of the JSON object that contains pos (walk backward). */
|
||||
static const char *find_json_object_start(const char *pos, const char *json_start) {
|
||||
if (!pos || !json_start || pos < json_start) return NULL;
|
||||
int depth = 0;
|
||||
const char *p = pos;
|
||||
while (p >= json_start) {
|
||||
if (*p == '}') depth++;
|
||||
else if (*p == '{') {
|
||||
if (depth == 0) return p;
|
||||
depth--;
|
||||
}
|
||||
p--;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Pointer just past the closing } of the object that starts at object_start. */
|
||||
static const char *find_json_object_end(const char *object_start) {
|
||||
if (!object_start || *object_start != '{') return NULL;
|
||||
int depth = 1;
|
||||
const char *p = object_start + 1;
|
||||
while (*p) {
|
||||
if (*p == '{') depth++;
|
||||
else if (*p == '}') {
|
||||
depth--;
|
||||
if (depth == 0) return p + 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ota_parse_release(const char *json_text, ota_release_t *out) {
|
||||
memset(out, 0, sizeof(*out));
|
||||
if (!json_text || !*json_text) {
|
||||
@@ -112,7 +144,7 @@ int ota_parse_release(const char *json_text, ota_release_t *out) {
|
||||
}
|
||||
snprintf(out->version, sizeof(out->version), "%d.%d.%d", sv.major, sv.minor, sv.patch);
|
||||
|
||||
/* Scan for OTA asset name then its browser_download_url in the same object-ish window. */
|
||||
/* Scan for OTA asset name then browser_download_url inside the same asset object. */
|
||||
const char *cursor = json_text;
|
||||
while ((cursor = strstr(cursor, "\"name\"")) != NULL) {
|
||||
char name[128];
|
||||
@@ -120,31 +152,36 @@ int ota_parse_release(const char *json_text, ota_release_t *out) {
|
||||
cursor += 6;
|
||||
continue;
|
||||
}
|
||||
if (ota_is_ota_asset_name(name)) {
|
||||
snprintf(out->asset_name, sizeof(out->asset_name), "%s", name);
|
||||
/* Prefer URL after this name key within a limited window. */
|
||||
const char *window_end = cursor + 800;
|
||||
if (window_end > json_text + strlen(json_text)) window_end = json_text + strlen(json_text);
|
||||
char url[512];
|
||||
const char *u = NULL;
|
||||
const char *scan = cursor;
|
||||
while (scan < window_end && (scan = strstr(scan, "\"browser_download_url\"")) != NULL &&
|
||||
scan < window_end) {
|
||||
if (find_json_string(scan, "browser_download_url", url, sizeof(url))) {
|
||||
u = url;
|
||||
break;
|
||||
}
|
||||
scan += 21;
|
||||
}
|
||||
if (!u || !*u) {
|
||||
set_reason(out->reason, sizeof(out->reason), "missing_download_url");
|
||||
return 0;
|
||||
}
|
||||
snprintf(out->download_url, sizeof(out->download_url), "%s", u);
|
||||
out->ok = 1;
|
||||
return 1;
|
||||
if (!ota_is_ota_asset_name(name)) {
|
||||
cursor += 6;
|
||||
continue;
|
||||
}
|
||||
cursor += 6;
|
||||
const char *asset_start = find_json_object_start(cursor, json_text);
|
||||
const char *asset_end =
|
||||
asset_start ? find_json_object_end(asset_start) : NULL;
|
||||
if (!asset_start || !asset_end || asset_end <= cursor) {
|
||||
cursor += 6;
|
||||
continue;
|
||||
}
|
||||
char url[512];
|
||||
const char *u = NULL;
|
||||
const char *scan = cursor;
|
||||
while (scan < asset_end &&
|
||||
(scan = strstr(scan, "\"browser_download_url\"")) != NULL && scan < asset_end) {
|
||||
if (find_json_string(scan, "browser_download_url", url, sizeof(url))) {
|
||||
u = url;
|
||||
break;
|
||||
}
|
||||
scan += 21;
|
||||
}
|
||||
if (!u || !*u) {
|
||||
cursor += 6;
|
||||
continue;
|
||||
}
|
||||
snprintf(out->asset_name, sizeof(out->asset_name), "%s", name);
|
||||
snprintf(out->download_url, sizeof(out->download_url), "%s", u);
|
||||
out->ok = 1;
|
||||
return 1;
|
||||
}
|
||||
set_reason(out->reason, sizeof(out->reason), "missing_ota_asset");
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user