| 232 | } |
| 233 | |
| 234 | static chd_file* OpenCHD(const std::string& filename, FileSystem::ManagedCFilePtr fp, Error* error, u32 recursion_level) |
| 235 | { |
| 236 | chd_file* chd; |
| 237 | ChdCoreFileWrapper* core_wrapper = new ChdCoreFileWrapper(fp.get(), nullptr); |
| 238 | // libchdr will take ownership of core_wrapper, and will close/free it on failure. |
| 239 | chd_error err = chd_open_core_file(core_wrapper->GetCoreFile(), CHD_OPEN_READ, nullptr, &chd); |
| 240 | if (err == CHDERR_NONE) |
| 241 | { |
| 242 | // core_wrapper should manage fp. |
| 243 | core_wrapper->SetFileOwner(true); |
| 244 | fp.release(); |
| 245 | return chd; |
| 246 | } |
| 247 | else if (err != CHDERR_REQUIRES_PARENT) |
| 248 | { |
| 249 | Console.Error(fmt::format("Failed to open CHD '{}': {}", filename, chd_error_string(err))); |
| 250 | Error::SetString(error, chd_error_string(err)); |
| 251 | return nullptr; |
| 252 | } |
| 253 | |
| 254 | if (recursion_level >= MAX_PARENTS) |
| 255 | { |
| 256 | Console.Error(fmt::format("Failed to open CHD '{}': Too many parent files", filename)); |
| 257 | Error::SetString(error, "Too many parent files"); |
| 258 | return nullptr; |
| 259 | } |
| 260 | |
| 261 | // Need to get the sha1 to look for. |
| 262 | chd_header header; |
| 263 | err = chd_read_header_file(fp.get(), &header); |
| 264 | if (err != CHDERR_NONE) |
| 265 | { |
| 266 | Console.Error(fmt::format("Failed to read CHD header '{}': {}", filename, chd_error_string(err))); |
| 267 | Error::SetString(error, chd_error_string(err)); |
| 268 | return nullptr; |
| 269 | } |
| 270 | |
| 271 | // Find a chd with a matching sha1 in the same directory. |
| 272 | // Have to do *.* and filter on the extension manually because Linux is case sensitive. |
| 273 | chd_file* parent_chd = nullptr; |
| 274 | const std::string parent_dir(Path::GetDirectory(filename)); |
| 275 | const std::unique_lock hash_cache_lock(s_chd_hash_cache_mutex); |
| 276 | |
| 277 | // Memoize which hashes came from what files, to avoid reading them repeatedly. |
| 278 | for (auto it = s_chd_hash_cache.begin(); it != s_chd_hash_cache.end(); ++it) |
| 279 | { |
| 280 | if (!StringUtil::compareNoCase(parent_dir, Path::GetDirectory(it->first))) |
| 281 | continue; |
| 282 | |
| 283 | if (!IsHeaderParentCHD(header, it->second)) |
| 284 | continue; |
| 285 | |
| 286 | // Re-check the header, it might have changed since we last opened. |
| 287 | chd_header parent_header; |
| 288 | auto parent_fp = FileSystem::OpenManagedSharedCFile(it->first.c_str(), "rb", FileSystem::FileShareMode::DenyWrite); |
| 289 | if (parent_fp && chd_read_header_file(parent_fp.get(), &parent_header) == CHDERR_NONE && |
| 290 | IsHeaderParentCHD(header, parent_header)) |
| 291 | { |
no test coverage detected