static unsigned int guessTabMultiplier(IFile &pckFile, IFile &tabFile) { // This tries to guess if the tab file contains (offset) or (offset/4) based on the last entry, // if multiplying it by 4 is greater than the pck file size it's (offset), otherwise (offset/4) auto pckSize = pckFile.size(); auto tabSize = tabFile.size(); if (tabSize < 4) { LogWarning("Tab size %zu too small for a single entry?", tabSize); return 0; } // Store the tab offset so we can restore the file state auto tabOffset = tabFile.tellg(); tabFile.seekg(tabSize - 4); uint32_t lastOffset; tabFile.read(reinterpret_cast<char *>(&lastOffset), sizeof(lastOffset)); if (!tabFile) { LogWarning("Failed to read last tab offset"); return 0; } tabFile.seekg(tabOffset); if (lastOffset * 4 >= pckSize) { return 1; } else { return 4; } }
bool load(IFile &file) { TRACE_FN; double usf; // uSeconds per frame this->video_data = file.readAll(); this->video_data_size = file.size(); auto video_path = file.systemPath(); this->file_path = video_path; LogInfo("Read %llu bytes from video", static_cast<unsigned long long>(this->video_data_size)); this->smk_ctx = smk_open_memory(reinterpret_cast<unsigned char *>(this->video_data.get()), static_cast<unsigned long>(this->video_data_size)); if (!this->smk_ctx) { LogWarning("Failed to read SMK file \"%s\"", video_path.cStr()); this->video_data.reset(); return false; } LogInfo("Successfully created SMK context"); if (smk_info_all(this->smk_ctx, nullptr, &this->frame_count, &usf)) { LogWarning("Failed to read SMK file info from \"%s\"", video_path.cStr()); this->video_data.reset(); smk_close(this->smk_ctx); this->smk_ctx = nullptr; return false; } this->frame_time = std::chrono::nanoseconds((unsigned int)(usf * 1000)); LogInfo("Video frame count %lu, ns per frame = %u (USF: %f)", this->frame_count, this->frame_time.count(), usf); unsigned long height, width; if (smk_info_video(this->smk_ctx, &width, &height, nullptr)) { LogWarning("Failed to read SMK video info from \"%s\"", video_path.cStr()); this->video_data.reset(); smk_close(this->smk_ctx); this->smk_ctx = nullptr; return false; } this->frame_size = {width, height}; LogInfo("Video frame size {%u,%u}", this->frame_size.x, this->frame_size.y); auto ret = smk_enable_video(this->smk_ctx, 1); if (ret == SMK_ERROR) { LogWarning("Error enabling video for \"%s\"", video_path.cStr()); return false; } unsigned char audio_track_mask; unsigned char channels[7]; unsigned char bitdepth[7]; unsigned long audio_rate[7]; ret = smk_info_audio(this->smk_ctx, &audio_track_mask, channels, bitdepth, audio_rate); if (ret == SMK_ERROR) { LogWarning("Error reading audio info for \"%s\"", video_path.cStr()); return false; } if (audio_track_mask & SMK_AUDIO_TRACK_0) { // WE only support a single track LogInfo("Audio track: channels %u depth %u rate %lu", (unsigned)channels[0], (unsigned)bitdepth[0], audio_rate[0]); } else { LogWarning("Unsupported audio track mask 0x%02x for \"%s\"", (unsigned)audio_track_mask, video_path.cStr()); return false; } switch (channels[0]) { case 1: // Mono this->audio_format.channels = 1; break; case 2: // Stereo this->audio_format.channels = 2; break; default: LogWarning("Unsupported audio channel count %u for \"%s\"", (unsigned)channels[0], video_path.cStr()); return false; } switch (bitdepth[0]) { case 8: this->audio_format.format = AudioFormat::SampleFormat::PCM_UINT8; this->audio_bytes_per_sample = 1; break; case 16: this->audio_format.format = AudioFormat::SampleFormat::PCM_SINT16; this->audio_bytes_per_sample = 2; break; default: LogWarning("Unsupported audio bit depth %u for \"%s\"", (unsigned)bitdepth[0], video_path.cStr()); return false; } this->audio_format.frequency = audio_rate[0]; ret = smk_enable_audio(this->smk_ctx, 0, 1); if (ret == SMK_ERROR) { LogWarning("Error enabling audio track 0 for \"%s\"", video_path.cStr()); } // Everything looks good return true; }