[], 'hardware' => [], 'systems' => [], 'online' => []]; if (!is_file($path)) { return $empty; } $raw = file_get_contents($path); if ($raw === false || trim($raw) === '') { return $empty; } $data = json_decode($raw, true); if (!is_array($data)) { return $empty; } // Prefer consoles; fall back to legacy videogames key. $consoles = $data['consoles'] ?? $data['videogames'] ?? []; return [ 'consoles' => is_array($consoles) ? $consoles : [], 'hardware' => is_array($data['hardware'] ?? null) ? $data['hardware'] : [], 'systems' => is_array($data['systems'] ?? null) ? $data['systems'] : [], 'online' => is_array($data['online'] ?? null) ? $data['online'] : [], ]; } function collection_image_src(string $image): string { $image = trim($image); if ($image === '') { return ''; } if (str_starts_with($image, 'http://') || str_starts_with($image, 'https://') || str_starts_with($image, '/')) { return $image; } if (str_contains($image, '..') || str_contains($image, "\0")) { return ''; } return '/gaming/collection/images/' . rawurlencode(basename($image)); } function collection_status_class(string $status): string { $slug = strtolower(preg_replace('/[^a-z0-9]+/i', '-', $status) ?? ''); $slug = trim($slug, '-'); return $slug !== '' ? 'status-' . $slug : 'status-other'; } function collection_safe_href(string $url): string { $url = trim($url); if ($url === '') { return ''; } if (str_starts_with($url, '/')) { return str_starts_with($url, '//') ? '' : $url; } $scheme = strtolower((string)(parse_url($url, PHP_URL_SCHEME) ?? '')); if (in_array($scheme, ['http', 'https', 'gemini', 'gopher'], true)) { return $url; } return ''; } /** * Inventory rows (name :: model) from a flat list (consoles or hardware). * @param list> $items * @return list}> */ function collection_inventory_rows(array $items): array { $rows = []; foreach ($items as $item) { if (!is_array($item)) continue; $name = trim((string)($item['name'] ?? '')); $model = trim((string)($item['model'] ?? '')); if ($name === '') continue; if ($model === '') $model = $name; $status = (string)($item['status'] ?? 'Owned'); $rows[] = [ 'left' => $name, 'right' => $model, 'status' => $status, 'notes' => (string)($item['notes'] ?? ''), 'image' => (string)($item['image'] ?? ''), 'url' => '', 'specs' => [ ['label' => t('collection.spec_system'), 'value' => $name], ['label' => t('collection.spec_status'), 'value' => $status], ], ]; } return $rows; } /** Consoles inventory. */ function collection_console_rows(array $data): array { return collection_inventory_rows($data['consoles'] ?? []); } /** Hardware inventory. */ function collection_hardware_inventory_rows(array $data): array { return collection_inventory_rows($data['hardware'] ?? []); } /** @deprecated use collection_console_rows */ function collection_videogame_rows(array $data): array { return collection_console_rows($data); } /** * Video games library: titles under systems (system :: title). * @return list}> */ function collection_games_library_rows(array $data): array { $rows = []; foreach (($data['systems'] ?? []) as $sys) { if (!is_array($sys)) continue; $left = (string)($sys['name'] ?? ''); foreach (($sys['games'] ?? []) as $game) { if (!is_array($game)) continue; $title = trim((string)($game['title'] ?? '')); if ($title === '') continue; $status = (string)($game['status'] ?? 'Owned'); $rows[] = [ 'left' => $left, 'right' => $title, 'status' => $status, 'notes' => (string)($game['notes'] ?? ''), 'image' => (string)($game['image'] ?? ''), 'url' => '', 'specs' => [ ['label' => t('collection.spec_system'), 'value' => $left], ['label' => t('collection.spec_status'), 'value' => $status], ], ]; } } return $rows; } /** @deprecated */ function collection_tech_rows(array $data): array { return collection_games_library_rows($data); } /** @deprecated */ function collection_hardware_rows(array $data): array { return collection_games_library_rows($data); } /** * Flatten online song packs into catalog rows. * @return list}> */ function collection_pack_rows(array $data): array { $rows = []; foreach ($data['online'] as $cat) { if (!is_array($cat)) continue; $left = (string)($cat['name'] ?? ''); foreach (($cat['entries'] ?? []) as $entry) { if (!is_array($entry)) continue; $title = trim((string)($entry['title'] ?? '')); if ($title === '') continue; $status = (string)($entry['status'] ?? 'Owned'); $url = (string)($entry['url'] ?? ''); $specs = [ ['label' => t('collection.spec_game'), 'value' => $left], ['label' => t('collection.spec_status'), 'value' => $status], ]; if ($url !== '') { $specs[] = ['label' => t('collection.spec_link'), 'value' => t('collection.spec_link_yes')]; } $rows[] = [ 'left' => $left, 'right' => $title, 'status' => $status, 'notes' => (string)($entry['notes'] ?? ''), 'image' => (string)($entry['image'] ?? ''), 'url' => $url, 'specs' => $specs, ]; } } return $rows; } /** * @param list $rows * @return list */ function collection_filter_wishlist(array $rows): array { return array_values(array_filter( $rows, static fn(array $r) => strcasecmp((string)($r['status'] ?? ''), 'Wishlist') === 0 )); } /** * Render one catalog row (photo + name + icon-style specs). * @param array{left:string,right:string,status:string,notes:string,image:string,url:string,specs?:array} $row */ function collection_render_row(array $row): void { $left = (string)($row['left'] ?? ''); $right = (string)($row['right'] ?? ''); $status = (string)($row['status'] ?? 'Owned'); $notes = (string)($row['notes'] ?? ''); $img = collection_image_src((string)($row['image'] ?? '')); $href = collection_safe_href((string)($row['url'] ?? '')); $specs = is_array($row['specs'] ?? null) ? $row['specs'] : []; $he = 'collection_he'; $h = 'collection_h'; ?>

:: ::

'en', 'ja' => 'ja', 'zh' => 'zh'][$lang] ?? 'en'; $suffix = ['en' => '', 'ja' => '_jp', 'zh' => '_zh'][$lang] ?? ''; $selfName = $baseFile . $suffix . '.php'; if ($baseFile === 'index' && $lang === 'en') { $selfUrl = 'https://www.sillylaird.ca/gaming/collection/'; $enUrl = 'https://www.sillylaird.ca/gaming/collection/'; } else { $selfUrl = 'https://www.sillylaird.ca/gaming/collection/' . $selfName; $enUrl = 'https://www.sillylaird.ca/gaming/collection/' . ($baseFile === 'index' ? '' : $baseFile . '.php'); if ($baseFile === 'index') { $enUrl = 'https://www.sillylaird.ca/gaming/collection/'; } } $jaUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_jp.php'; $zhUrl = 'https://www.sillylaird.ca/gaming/collection/' . $baseFile . '_zh.php'; if ($baseFile === 'index') { $jaUrl = 'https://www.sillylaird.ca/gaming/collection/index_jp.php'; $zhUrl = 'https://www.sillylaird.ca/gaming/collection/index_zh.php'; } $h = 'collection_h'; // Video Games / Consoles / Hardware / Song Packs / Wishlist if ($lang === 'ja') { $hrefGames = '/gaming/collection/index_jp.php'; $hrefCons = '/gaming/collection/consoles_jp.php'; $hrefHw = '/gaming/collection/hardware_jp.php'; $hrefPk = '/gaming/collection/packs_jp.php'; $hrefWl = '/gaming/collection/wishlist_jp.php'; } elseif ($lang === 'zh') { $hrefGames = '/gaming/collection/index_zh.php'; $hrefCons = '/gaming/collection/consoles_zh.php'; $hrefHw = '/gaming/collection/hardware_zh.php'; $hrefPk = '/gaming/collection/packs_zh.php'; $hrefWl = '/gaming/collection/wishlist_zh.php'; } else { $hrefGames = '/gaming/collection/'; $hrefCons = '/gaming/collection/consoles.php'; $hrefHw = '/gaming/collection/hardware.php'; $hrefPk = '/gaming/collection/packs.php'; $hrefWl = '/gaming/collection/wishlist.php'; } $act = static fn(string $key) => $active === $key ? ' class="gc-menu-active" id="gc-main"' : ''; ?> <?= collection_he($pageTitle) ?> $lang, 'title' => $pageTitle, 'desc' => $pageDesc, 'base' => 'gaming/collection/' . $baseFile, 'self_url' => $selfUrl, 'en_url' => $enUrl, 'x_default' => $enUrl, 'ja_url' => $jaUrl, 'zh_url' => $zhUrl, ]; include __DIR__ . '/../../partials/head_meta.php'; ?>