Initial commit
This commit is contained in:
commit
6072b2ba29
844 changed files with 220532 additions and 0 deletions
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
187
app/build.gradle.kts
Normal file
187
app/build.gradle.kts
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import java.util.Properties
|
||||
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
alias(libs.plugins.kotlin.compose)
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.1.20"
|
||||
alias(libs.plugins.kotlin.ksp)
|
||||
}
|
||||
|
||||
val localProperties = Properties()
|
||||
val localPropertiesFile = rootProject.file("local.properties")
|
||||
if (localPropertiesFile.exists()) {
|
||||
localPropertiesFile.inputStream().use { localProperties.load(it) }
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.aryan.reader"
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.aryan.reader"
|
||||
minSdk = 26
|
||||
targetSdk = 35
|
||||
versionCode = 32
|
||||
versionName = "1.0.31"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
cppFlags += ""
|
||||
}
|
||||
}
|
||||
buildConfigField("boolean", "IS_PRO", "false")
|
||||
}
|
||||
|
||||
flavorDimensions += "version"
|
||||
productFlavors {
|
||||
create("oss") {
|
||||
dimension = "version"
|
||||
applicationIdSuffix = ".oss"
|
||||
versionNameSuffix = "-oss"
|
||||
buildConfigField("String", "AI_WORKER_URL", "\"\"")
|
||||
buildConfigField("String", "VERIFIER_WORKER_URL", "\"\"")
|
||||
buildConfigField("String", "FEEDBACK_WORKER_URL", "\"\"")
|
||||
buildConfigField("boolean", "IS_PRO", "false")
|
||||
}
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
}
|
||||
}
|
||||
|
||||
signingConfigs {
|
||||
create("release") {
|
||||
val storePath = localProperties.getProperty("MYAPP_RELEASE_STORE_FILE")
|
||||
if (!storePath.isNullOrEmpty()) {
|
||||
storeFile = file(storePath)
|
||||
storePassword = localProperties.getProperty("MYAPP_RELEASE_STORE_PASSWORD")
|
||||
keyAlias = localProperties.getProperty("MYAPP_RELEASE_KEY_ALIAS")
|
||||
keyPassword = localProperties.getProperty("MYAPP_RELEASE_KEY_PASSWORD")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
signingConfig = signingConfigs.getByName("release")
|
||||
isMinifyEnabled = true
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "17"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
}
|
||||
publishing {
|
||||
singleVariant("release") {
|
||||
}
|
||||
}
|
||||
externalNativeBuild {
|
||||
cmake {
|
||||
path = file("src/main/cpp/CMakeLists.txt")
|
||||
version = "3.22.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
//noinspection UseTomlInstead
|
||||
dependencies {
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
implementation(platform(libs.androidx.compose.bom))
|
||||
implementation(libs.androidx.ui)
|
||||
implementation(libs.androidx.ui.graphics)
|
||||
implementation(libs.androidx.ui.tooling.preview)
|
||||
implementation(libs.androidx.material3)
|
||||
implementation(libs.androidx.material3.window.size.class1.android)
|
||||
implementation(libs.androidx.credentials)
|
||||
|
||||
androidTestImplementation(libs.androidx.junit)
|
||||
androidTestImplementation(libs.androidx.espresso.core)
|
||||
androidTestImplementation(platform(libs.androidx.compose.bom))
|
||||
androidTestImplementation(libs.androidx.ui.test.junit4)
|
||||
androidTestImplementation("androidx.test:rules:1.7.0")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-web:3.7.0")
|
||||
androidTestImplementation("com.google.truth:truth:1.4.2")
|
||||
androidTestImplementation("androidx.navigation:navigation-testing:2.9.6")
|
||||
androidTestImplementation("io.mockk:mockk-android:1.13.11") {
|
||||
exclude(group = "org.junit.jupiter")
|
||||
}
|
||||
androidTestImplementation(libs.kotlinx.coroutines.test)
|
||||
|
||||
debugImplementation(libs.androidx.ui.test.manifest)
|
||||
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
implementation("androidx.compose.material:material-icons-extended:1.7.8")
|
||||
|
||||
implementation("androidx.appcompat:appcompat:1.7.1")
|
||||
|
||||
//noinspection GradleDependency (Updating these might cause the custom toolbox in pagination to break)
|
||||
implementation("androidx.navigation:navigation-compose:2.9.2")
|
||||
//noinspection GradleDependency
|
||||
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.9.2")
|
||||
//noinspection GradleDependency
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.9.2")
|
||||
//noinspection GradleDependency
|
||||
implementation("androidx.compose.material3.adaptive:adaptive:1.2.0-alpha11")
|
||||
|
||||
implementation("org.jsoup:jsoup:1.17.2")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.7.3")
|
||||
|
||||
implementation("io.coil-kt:coil-compose:2.7.0")
|
||||
implementation("io.coil-kt:coil-svg:2.6.0")
|
||||
|
||||
implementation(project(":pdfiumandroid"))
|
||||
|
||||
implementation("androidx.media3:media3-exoplayer:1.8.0")
|
||||
implementation("androidx.media3:media3-session:1.8.0")
|
||||
implementation("androidx.media3:media3-ui:1.8.0")
|
||||
|
||||
implementation("androidx.work:work-runtime-ktx:2.10.5")
|
||||
implementation("androidx.compose.runtime:runtime-livedata:1.9.3")
|
||||
|
||||
implementation("org.slf4j:slf4j-android:1.7.36")
|
||||
|
||||
implementation("org.commonmark:commonmark:0.22.0")
|
||||
|
||||
implementation("com.jakewharton.timber:timber:5.0.1")
|
||||
|
||||
implementation("com.tom-roush:pdfbox-android:2.0.27.0")
|
||||
|
||||
implementation("androidx.paging:paging-runtime-ktx:3.3.6")
|
||||
implementation("androidx.paging:paging-compose:3.3.6")
|
||||
implementation("androidx.room:room-paging:2.7.1")
|
||||
|
||||
// Flexmark for Markdown parsing (MD -> HTML)
|
||||
implementation("com.vladsch.flexmark:flexmark:0.64.8")
|
||||
implementation("com.vladsch.flexmark:flexmark-ext-tables:0.64.8")
|
||||
implementation("com.vladsch.flexmark:flexmark-ext-gfm-strikethrough:0.64.8")
|
||||
implementation("com.vladsch.flexmark:flexmark-ext-gfm-tasklist:0.64.8")
|
||||
implementation("com.vladsch.flexmark:flexmark-ext-autolink:0.64.8")
|
||||
|
||||
implementation("androidx.documentfile:documentfile:1.0.1")
|
||||
implementation("androidx.browser:browser:1.8.0")
|
||||
}
|
||||
81
app/proguard-rules.pro
vendored
Normal file
81
app/proguard-rules.pro
vendored
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
-renamesourcefileattribute SourceFile
|
||||
|
||||
# Fix for Firestore crash in release builds.
|
||||
# This prevents ProGuard from removing the default constructor and fields
|
||||
# that Firestore needs for data serialization and deserialization.
|
||||
-keepattributes Signature
|
||||
-keep class com.google.firebase.firestore.** { *; }
|
||||
-keepnames class com.google.protobuf.** { *; }
|
||||
|
||||
# IMPORTANT: Keep all your data model classes that you use with Firestore.
|
||||
# The following rule covers all classes in your 'data' package.
|
||||
-keep class com.aryan.reader.data.** {
|
||||
<init>();
|
||||
*;
|
||||
}
|
||||
|
||||
# Keep MobiParser inner classes that are accessed from JNI.
|
||||
# ProGuard/R8 can't detect this usage, so we must keep them explicitly
|
||||
# to prevent renaming/removal in release builds.
|
||||
-keep class com.aryan.reader.epub.MobiParser$* {
|
||||
<init>(...);
|
||||
*;
|
||||
}
|
||||
|
||||
# Keep classes for Google Sign In and Credential Manager
|
||||
-keep class com.google.android.libraries.identity.googleid.** { *; }
|
||||
-keep class com.google.android.gms.auth.api.identity.** { *; }
|
||||
-keep class androidx.credentials.** { *; }
|
||||
|
||||
# Keep classes for Firebase Auth
|
||||
-keep class com.google.firebase.auth.** { *; }
|
||||
|
||||
# These rules can help prevent gRPC-related issues in release builds.
|
||||
-keep class io.grpc.** { *; }
|
||||
-dontwarn com.squareup.okhttp.**
|
||||
|
||||
#noinspection ShrinkerUnresolvedReference
|
||||
-dontwarn com.google.protobuf.GeneratedMessageV3
|
||||
-keepclassmembers class * extends com.google.protobuf.GeneratedMessageV3 {
|
||||
<fields>;
|
||||
}
|
||||
|
||||
# Keep GSON TypeToken for proper JSON serialization/deserialization in release builds.
|
||||
# This prevents R8 from stripping generic type information needed by Gson.
|
||||
-keep class com.google.gson.reflect.TypeToken { *; }
|
||||
-keep class * extends com.google.gson.reflect.TypeToken
|
||||
|
||||
-keep class com.aryan.reader.paginatedreader.Woff2Converter { *; }
|
||||
|
||||
-dontwarn com.gemalto.jp2.**
|
||||
|
||||
# Flexmark Markdown parser rules
|
||||
-keep class com.vladsch.flexmark.** { *; }
|
||||
-keepnames class com.vladsch.flexmark.** { *; }
|
||||
-keepclassmembers class com.vladsch.flexmark.** { *; }
|
||||
|
||||
-dontwarn java.awt.**
|
||||
-dontwarn javax.swing.**
|
||||
-dontwarn javax.imageio.**
|
||||
|
||||
-keepattributes Signature, EnclosingMethod, InnerClasses, *Annotation*
|
||||
118
app/src/androidTest/java/com/aryan/reader/AppNavigationTest.kt
Normal file
118
app/src/androidTest/java/com/aryan/reader/AppNavigationTest.kt
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
// AppNavigationTest.kt
|
||||
package com.aryan.reader
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSizeClassApi
|
||||
import androidx.compose.material3.windowsizeclass.WindowSizeClass
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.unit.DpSize
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.compose.ComposeNavigator
|
||||
import androidx.navigation.testing.TestNavHostController
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.aryan.reader.epub.EpubBook
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class AppNavigationTest {
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createComposeRule()
|
||||
|
||||
private lateinit var navController: TestNavHostController
|
||||
private val fakeUiState = MutableStateFlow(ReaderScreenState())
|
||||
|
||||
// Mock ViewModel that uses the fake state
|
||||
private val fakeViewModel: MainViewModel = object : MainViewModel(
|
||||
ApplicationProvider.getApplicationContext()
|
||||
) {
|
||||
override val uiState = fakeUiState
|
||||
override fun clearSelectedFile() {
|
||||
fakeUiState.value = fakeUiState.value.copy(
|
||||
selectedFileType = null,
|
||||
selectedPdfUri = null,
|
||||
selectedEpubBook = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3WindowSizeClassApi::class)
|
||||
@Before
|
||||
fun setup() {
|
||||
composeTestRule.setContent {
|
||||
navController = TestNavHostController(LocalContext.current)
|
||||
navController.navigatorProvider.addNavigator(ComposeNavigator())
|
||||
AppNavigation(
|
||||
navController = navController,
|
||||
windowSizeClass = WindowSizeClass.calculateFromSize(DpSize(400.dp, 800.dp)),
|
||||
viewModel = fakeViewModel
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appNavigation_defaultStartDestination_isMainRoute() {
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
assertEquals(AppDestinations.MAIN_ROUTE, currentRoute)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appNavigation_whenPdfSelected_navigatesToPdfViewer() {
|
||||
// Trigger state change
|
||||
fakeUiState.value = ReaderScreenState(
|
||||
selectedFileType = FileType.PDF,
|
||||
selectedPdfUri = Uri.parse("content://dummy.pdf")
|
||||
)
|
||||
|
||||
// Let compose recompose and run LaunchedEffect
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
assertEquals(AppDestinations.PDF_VIEWER_ROUTE, currentRoute)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appNavigation_whenEpubSelected_navigatesToEpubReader() {
|
||||
// Trigger state change
|
||||
fakeUiState.value = ReaderScreenState(
|
||||
selectedFileType = FileType.EPUB,
|
||||
selectedEpubBook = EpubBook(
|
||||
fileName = "dummy.epub",
|
||||
title = "Dummy Book",
|
||||
author = "Author",
|
||||
language = "en",
|
||||
coverImage = null
|
||||
)
|
||||
)
|
||||
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
assertEquals(AppDestinations.EPUB_READER_ROUTE, currentRoute)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun appNavigation_whenFileCleared_navigatesBackToMain() {
|
||||
// First, navigate to PDF viewer
|
||||
fakeUiState.value = ReaderScreenState(
|
||||
selectedFileType = FileType.PDF,
|
||||
selectedPdfUri = Uri.parse("content://dummy.pdf")
|
||||
)
|
||||
composeTestRule.waitForIdle()
|
||||
assertEquals(AppDestinations.PDF_VIEWER_ROUTE, navController.currentBackStackEntry?.destination?.route)
|
||||
|
||||
// Then, trigger the clear action (simulating onNavigateBack)
|
||||
fakeViewModel.clearSelectedFile()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
val currentRoute = navController.currentBackStackEntry?.destination?.route
|
||||
assertEquals(AppDestinations.MAIN_ROUTE, currentRoute)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.aryan.reader
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
|
||||
/**
|
||||
* A JUnit TestRule that sets the Main dispatcher to a TestDispatcher for the duration of a test.
|
||||
* This allows tests to execute coroutines on the Main dispatcher without needing a real Android environment.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MainDispatcherRule(
|
||||
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
) : TestWatcher() {
|
||||
override fun starting(description: Description) {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
}
|
||||
|
||||
override fun finished(description: Description) {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
package com.aryan.reader.epubreader
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class ChapterWebViewBridgeTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
@Test
|
||||
fun cfiJsBridge_onCfiExtracted_callsCallbackWithCorrectCfi() {
|
||||
var receivedCfi = ""
|
||||
val bridge = CfiJsBridge(
|
||||
onCfiReady = { cfi -> receivedCfi = cfi },
|
||||
onCfiForBookmarkReady = {}
|
||||
)
|
||||
|
||||
val cfi = "/4/2[chapter1]/6:10"
|
||||
val jsonResponse = JSONObject().apply {
|
||||
put("cfi", cfi)
|
||||
put("log", JSONArray(listOf("log message 1", "log message 2")))
|
||||
}.toString()
|
||||
|
||||
bridge.onCfiExtracted(jsonResponse)
|
||||
|
||||
assertThat(receivedCfi).isEqualTo(cfi)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cfiJsBridge_onCfiExtracted_withInvalidJson_callsCallbackWithFallbackCfi() {
|
||||
var receivedCfi = ""
|
||||
val bridge = CfiJsBridge(
|
||||
onCfiReady = { cfi -> receivedCfi = cfi },
|
||||
onCfiForBookmarkReady = {}
|
||||
)
|
||||
|
||||
val invalidJson = "this is not json"
|
||||
|
||||
bridge.onCfiExtracted(invalidJson)
|
||||
|
||||
assertThat(receivedCfi).isEqualTo("/4")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cfiJsBridge_onCfiExtracted_withEmptyCfi_callsCallbackWithCfi() {
|
||||
var receivedCfi: String? = null
|
||||
val bridge = CfiJsBridge(
|
||||
onCfiReady = { cfi -> receivedCfi = cfi },
|
||||
onCfiForBookmarkReady = {}
|
||||
)
|
||||
|
||||
val jsonResponse = JSONObject().apply {
|
||||
put("cfi", "")
|
||||
put("log", JSONArray())
|
||||
}.toString()
|
||||
|
||||
bridge.onCfiExtracted(jsonResponse)
|
||||
|
||||
// The handler is only called if the CFI is not blank, so it should remain null
|
||||
assertThat(receivedCfi).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ttsJsBridge_onStructuredTextExtracted_callsHandlerWithJson() = runTest {
|
||||
var receivedJson: String? = null
|
||||
val bridge = TtsJsBridge(
|
||||
scope = this,
|
||||
ttsStructuredTextHandler = { json -> receivedJson = json }
|
||||
)
|
||||
val jsonPayload = "[{\"text\":\"Hello world\",\"cfi\":\"/4/2\"}]"
|
||||
bridge.onStructuredTextExtracted(jsonPayload)
|
||||
advanceUntilIdle()
|
||||
assertThat(receivedJson).isEqualTo(jsonPayload)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun ttsJsBridge_onStructuredTextExtracted_withEmptyJson_callsHandlerWithEmptyArray() = runTest {
|
||||
var receivedJson: String? = null
|
||||
val bridge = TtsJsBridge(
|
||||
scope = this,
|
||||
ttsStructuredTextHandler = { json -> receivedJson = json }
|
||||
)
|
||||
val jsonPayload = ""
|
||||
bridge.onStructuredTextExtracted(jsonPayload)
|
||||
advanceUntilIdle()
|
||||
assertThat(receivedJson).isEqualTo("[]")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun snippetJsBridge_onSnippetExtracted_callsCallbackWithCfiAndSnippet() {
|
||||
var receivedCfi = ""
|
||||
var receivedSnippet = ""
|
||||
val bridge = SnippetJsBridge(
|
||||
onSnippetReady = { cfi, snippet ->
|
||||
receivedCfi = cfi
|
||||
receivedSnippet = snippet
|
||||
}
|
||||
)
|
||||
|
||||
val cfi = "/4/8:5"
|
||||
val snippet = "This is the bookmark snippet."
|
||||
bridge.onSnippetExtracted(cfi, snippet)
|
||||
|
||||
assertThat(receivedCfi).isEqualTo(cfi)
|
||||
assertThat(receivedSnippet).isEqualTo(snippet)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun progressJsBridge_onTopChunkUpdated_invokesCallback() {
|
||||
var updatedChunk = -1
|
||||
val bridge = ProgressJsBridge(onTopChunkUpdated = { index -> updatedChunk = index })
|
||||
|
||||
bridge.updateTopChunk(5)
|
||||
assertThat(updatedChunk).isEqualTo(5)
|
||||
|
||||
bridge.updateTopChunk(10)
|
||||
assertThat(updatedChunk).isEqualTo(10)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun progressJsBridge_onTopChunkUpdated_doesNotCallForSameIndex() {
|
||||
var callCount = 0
|
||||
val bridge = ProgressJsBridge(onTopChunkUpdated = { callCount++ })
|
||||
|
||||
bridge.updateTopChunk(3)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
|
||||
// Reporting the same index should not trigger the callback again
|
||||
bridge.updateTopChunk(3)
|
||||
assertThat(callCount).isEqualTo(1)
|
||||
|
||||
bridge.updateTopChunk(4)
|
||||
assertThat(callCount).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun aiJsBridge_onContentExtracted_invokesCallback() = runTest {
|
||||
var receivedContent: String? = null
|
||||
val bridge = AiJsBridge(
|
||||
scope = this,
|
||||
onContentReady = { content -> receivedContent = content }
|
||||
)
|
||||
val content = "This is the chapter content for summarization."
|
||||
bridge.onContentExtractedForSummarization(content)
|
||||
advanceUntilIdle()
|
||||
assertThat(receivedContent).isEqualTo(content)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
package com.aryan.reader.epubreader
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.aryan.reader.epub.EpubChapter
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EpubReaderBookmarkTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
private val testBookTitle = "My Test Book"
|
||||
private val chapters = listOf(
|
||||
EpubChapter(chapterId = "ch1", title = "Chapter 1", htmlFilePath = "", absPath = "", htmlContent = "", plainTextContent = ""),
|
||||
EpubChapter(chapterId = "ch2", title = "Chapter 2", htmlFilePath = "", absPath = "", htmlContent = "", plainTextContent = "")
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
// Clear any old prefs to ensure a clean slate for each test
|
||||
val prefs = context.getSharedPreferences("epub_reader_bookmarks", Context.MODE_PRIVATE)
|
||||
prefs.edit().clear().apply()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadBookmarks_withValidJson_parsesCorrectly() {
|
||||
val bookmark1 = JSONObject().apply {
|
||||
put("cfi", "/4/2:10")
|
||||
put("chapterTitle", "Chapter 1")
|
||||
put("snippet", "A snippet of text")
|
||||
put("chapterIndex", 0)
|
||||
}
|
||||
val bookmark2 = JSONObject().apply {
|
||||
put("cfi", "/6/4:22")
|
||||
put("chapterTitle", "Chapter 2")
|
||||
put("snippet", "Another snippet")
|
||||
put("chapterIndex", 1)
|
||||
}
|
||||
val bookmarksJson = JSONArray(listOf(bookmark1.toString(), bookmark2.toString())).toString()
|
||||
|
||||
val bookmarks = loadBookmarks(context, testBookTitle, chapters, bookmarksJson)
|
||||
|
||||
assertThat(bookmarks).hasSize(2)
|
||||
assertThat(bookmarks).contains(
|
||||
Bookmark(
|
||||
cfi = "/4/2:10",
|
||||
chapterTitle = "Chapter 1",
|
||||
snippet = "A snippet of text",
|
||||
pageInChapter = null,
|
||||
totalPagesInChapter = null,
|
||||
chapterIndex = 0
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadBookmarks_withInvalidJson_returnsEmptySet() {
|
||||
val invalidJson = "[{\"cfi\": \"/4/2:10\", snippet: \"invalid json\"}]" // snippet value not in quotes
|
||||
val bookmarks = loadBookmarks(context, testBookTitle, chapters, invalidJson)
|
||||
assertThat(bookmarks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadBookmarks_withMissingChapterIndex_calculatesItFromTitle() {
|
||||
val bookmark1 = JSONObject().apply {
|
||||
put("cfi", "/6/4:22")
|
||||
put("chapterTitle", "Chapter 2") // This should map to index 1
|
||||
put("snippet", "Another snippet")
|
||||
}
|
||||
val bookmarksJson = JSONArray(listOf(bookmark1.toString())).toString()
|
||||
|
||||
val bookmarks = loadBookmarks(context, testBookTitle, chapters, bookmarksJson)
|
||||
|
||||
assertThat(bookmarks).hasSize(1)
|
||||
val loadedBookmark = bookmarks.first()
|
||||
assertThat(loadedBookmark.chapterIndex).isEqualTo(1)
|
||||
assertThat(loadedBookmark.chapterTitle).isEqualTo("Chapter 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun loadBookmarks_withNullJson_fallsBackToSharedPreferences() {
|
||||
// This test doesn't write to shared prefs, so it should return an empty set.
|
||||
val bookmarks = loadBookmarks(context, testBookTitle, chapters, null)
|
||||
assertThat(bookmarks).isEmpty()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
// app/src/androidTest/java/com/aryan/reader/epubreader/EpubReaderLogicTest.kt
|
||||
package com.aryan.reader.epubreader
|
||||
|
||||
import android.content.Context
|
||||
import timber.log.Timber
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.aryan.reader.SearchResult
|
||||
import com.aryan.reader.epub.EpubBook
|
||||
import com.aryan.reader.epub.EpubChapter
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jsoup.Jsoup
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class EpubReaderLogicTest {
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var testDir: File
|
||||
private lateinit var mockEpubBook: EpubBook
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
testDir = File(context.cacheDir, "test_epub").apply { mkdirs() }
|
||||
|
||||
// Create dummy chapter files
|
||||
val chapter1File = File(testDir, "chapter1.html")
|
||||
chapter1File.writeText("<html><body><p>A simple Test case.</p></body></html>")
|
||||
|
||||
val chapter2File = File(testDir, "chapter2.html")
|
||||
chapter2File.writeText("<html><body><p>Another test case here.</p><p>The word Test appears twice.</p></body></html>")
|
||||
|
||||
mockEpubBook = EpubBook(
|
||||
fileName = "test.epub",
|
||||
title = "Test Book",
|
||||
author = "Tester",
|
||||
language = "en",
|
||||
coverImage = null,
|
||||
extractionBasePath = testDir.absolutePath,
|
||||
chapters = listOf(
|
||||
EpubChapter(
|
||||
chapterId = "ch1",
|
||||
absPath = chapter1File.absolutePath,
|
||||
title = "Chapter 1",
|
||||
htmlFilePath = "chapter1.html",
|
||||
plainTextContent = "",
|
||||
htmlContent = ""
|
||||
),
|
||||
EpubChapter(
|
||||
chapterId = "ch2",
|
||||
absPath = chapter2File.absolutePath,
|
||||
title = "Chapter 2",
|
||||
htmlFilePath = "chapter2.html",
|
||||
plainTextContent = "",
|
||||
htmlContent = ""
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
testDir.deleteRecursively()
|
||||
}
|
||||
|
||||
private suspend fun searchEpub(book: EpubBook, query: String): List<SearchResult> {
|
||||
val TAG = "EpubReaderLogicTest"
|
||||
Timber.d("Starting search for query: '$query'")
|
||||
return withContext(Dispatchers.IO) {
|
||||
val results = mutableListOf<SearchResult>()
|
||||
book.chapters.forEachIndexed { chapterIndex, chapter ->
|
||||
try {
|
||||
val fullPath = "${book.extractionBasePath}/${chapter.htmlFilePath}"
|
||||
Timber.d("Chapter ${chapterIndex + 1}: Checking path '$fullPath'")
|
||||
val htmlFile = File(fullPath)
|
||||
if (!htmlFile.exists()) {
|
||||
Timber.e("File does not exist: $fullPath")
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
val doc = Jsoup.parse(htmlFile, "UTF-8")
|
||||
val bodyChildren = doc.body().children().toList()
|
||||
val chunks = bodyChildren.chunked(20)
|
||||
|
||||
chunks.forEachIndexed { chunkIndex, chunkOfElements ->
|
||||
val chunkHtml = chunkOfElements.joinToString(separator = "\n") { it.outerHtml() }
|
||||
val content = Jsoup.parse(chunkHtml).text()
|
||||
var lastIndex = -1
|
||||
|
||||
while (true) {
|
||||
lastIndex = content.indexOf(query, startIndex = lastIndex + 1, ignoreCase = true)
|
||||
if (lastIndex == -1) break
|
||||
|
||||
Timber.d("Found potential match for '$query' at index $lastIndex.")
|
||||
val isWordStart = lastIndex == 0 || !content[lastIndex - 1].isLetterOrDigit()
|
||||
Timber.d("Is it a word start? -> $isWordStart")
|
||||
if (isWordStart) {
|
||||
Timber.d("Match is a word start. Adding to results.")
|
||||
val snippetStart = max(0, lastIndex - 35)
|
||||
val snippetEnd = min(content.length, lastIndex + query.length + 35)
|
||||
val rawSnippet = content.substring(snippetStart, snippetEnd)
|
||||
val annotatedSnippet = buildAnnotatedString {
|
||||
append(rawSnippet)
|
||||
val highlightStart = content.indexOf(query, lastIndex, ignoreCase = true) - snippetStart
|
||||
val highlightEnd = highlightStart + query.length
|
||||
addStyle(
|
||||
style = SpanStyle(fontWeight = FontWeight.Bold),
|
||||
start = highlightStart,
|
||||
end = highlightEnd
|
||||
)
|
||||
}
|
||||
results.add(
|
||||
SearchResult(
|
||||
locationInSource = chapterIndex,
|
||||
locationTitle = chapter.title,
|
||||
snippet = annotatedSnippet,
|
||||
query = query,
|
||||
occurrenceIndexInLocation = results.count { it.locationInSource == chapterIndex },
|
||||
chunkIndex = chunkIndex
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Error during search in chapter ${chapter.title}", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
Timber.d("Search finished. Total results found: ${results.size}")
|
||||
results
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_findsCorrectResults() = runBlocking {
|
||||
val results = searchEpub(mockEpubBook, "case")
|
||||
assertThat(results).hasSize(2)
|
||||
assertThat(results.count { it.locationTitle == "Chapter 1" }).isEqualTo(1)
|
||||
assertThat(results.count { it.locationTitle == "Chapter 2" }).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_isCaseInsensitive() = runBlocking {
|
||||
val results = searchEpub(mockEpubBook, "test")
|
||||
assertThat(results).hasSize(3)
|
||||
assertThat(results[0].locationTitle).isEqualTo("Chapter 1")
|
||||
assertThat(results[1].locationTitle).isEqualTo("Chapter 2")
|
||||
assertThat(results[2].locationTitle).isEqualTo("Chapter 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_noResultsFound() = runBlocking {
|
||||
val results = searchEpub(mockEpubBook, "nonexistent")
|
||||
assertThat(results).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_createsCorrectSnippetHighlight() = runBlocking {
|
||||
val query = "Test"
|
||||
mockEpubBook.chapters.first()
|
||||
val content = "A simple Test case."
|
||||
val annotatedString = buildAnnotatedStringWithHighlight(content, query)
|
||||
|
||||
val spanStyles = annotatedString.spanStyles
|
||||
assertThat(spanStyles).hasSize(1)
|
||||
|
||||
val style = spanStyles.first().item
|
||||
assertThat(style.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
|
||||
val start = spanStyles.first().start
|
||||
val end = spanStyles.first().end
|
||||
assertThat(annotatedString.substring(start, end)).isEqualTo(query)
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun buildAnnotatedStringWithHighlight(content: String, query: String): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
append(content)
|
||||
val highlightStart = content.indexOf(query, ignoreCase = true)
|
||||
if (highlightStart != -1) {
|
||||
addStyle(
|
||||
style = SpanStyle(fontWeight = FontWeight.Bold),
|
||||
start = highlightStart,
|
||||
end = highlightStart + query.length
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.aryan.reader.epubreader
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
|
||||
/**
|
||||
* A JUnit TestRule that sets the Main dispatcher to a TestDispatcher for the duration of a test.
|
||||
* This allows tests to execute coroutines on the Main dispatcher without needing a real Android environment.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MainDispatcherRule(
|
||||
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
) : TestWatcher() {
|
||||
override fun starting(description: Description) {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
}
|
||||
|
||||
override fun finished(description: Description) {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
// CssParserTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.isSpecified
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.em
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class CssParserTest {
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesNamedColorsCorrectly() {
|
||||
assertThat(CssParser.parseColor("red")).isEqualTo(Color.Red)
|
||||
assertThat(CssParser.parseColor("black")).isEqualTo(Color.Black)
|
||||
assertThat(CssParser.parseColor("transparent")).isEqualTo(Color.Transparent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles3DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#F0C")).isEqualTo(Color(0xFFFF00CC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles6DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#FF00CC")).isEqualTo(Color(0xFFFF00CC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handles8DigitHexCodes() {
|
||||
assertThat(CssParser.parseColor("#80FF00CC")).isEqualTo(Color(0x80FF00CC))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesRgbFunction() {
|
||||
assertThat(CssParser.parseColor("rgb(255, 0, 204)")).isEqualTo(Color(255, 0, 204))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_handlesRgbaFunction() {
|
||||
assertThat(CssParser.parseColor("rgba(255, 0, 204, 0.5)")).isEqualTo(Color(255, 0, 204, 128))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseColor_returnsNullForInvalidInput() {
|
||||
assertThat(CssParser.parseColor("not a color")).isNull()
|
||||
assertThat(CssParser.parseColor("#12345")).isNull()
|
||||
assertThat(CssParser.parseColor("rgb(1,2)")).isNull()
|
||||
}
|
||||
|
||||
private val dummyConstraints = androidx.compose.ui.unit.Constraints()
|
||||
private val baseFontSize = 16f
|
||||
private val density = 1f
|
||||
|
||||
@Test
|
||||
fun parse_handlesSimpleRule() {
|
||||
val css = "p { color: red; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val rules = result.rules.byTag["p"]
|
||||
assertThat(rules).hasSize(1)
|
||||
assertThat(rules?.first()?.style?.spanStyle?.color).isEqualTo(Color.Red)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesMultipleSelectors() {
|
||||
val css = "h1, h2, h3 { font-weight: bold; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.rules.byTag["h1"]).hasSize(1)
|
||||
assertThat(result.rules.byTag["h2"]).hasSize(1)
|
||||
assertThat(result.rules.byTag["h3"]).hasSize(1)
|
||||
assertThat(result.rules.byTag["h1"]?.first()?.style?.spanStyle?.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
assertThat(result.rules.byTag["h2"]?.first()?.style?.spanStyle?.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
assertThat(result.rules.byTag["h3"]?.first()?.style?.spanStyle?.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesImportantRules() {
|
||||
val css = "p { color: red !important; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val importantRule = result.rules.byTag["p"]?.find { it.selector.specificity >= 10000 }
|
||||
assertThat(importantRule).isNotNull()
|
||||
assertThat(importantRule!!.style.spanStyle.color).isEqualTo(Color.Red)
|
||||
val normalRule = result.rules.byTag["p"]?.find { it.selector.specificity < 10000 }
|
||||
assertThat(normalRule).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_createsBothNormalAndImportantRulesWhenMixed() {
|
||||
val css = "p { color: blue; background-color: white !important; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val rules = result.rules.byTag["p"]
|
||||
assertThat(rules).hasSize(2)
|
||||
|
||||
val importantRule = rules?.find { it.selector.specificity >= 10000 }
|
||||
assertThat(importantRule).isNotNull()
|
||||
assertThat(importantRule!!.style.blockStyle.backgroundColor).isEqualTo(Color.White)
|
||||
assertThat(importantRule.style.spanStyle.color.isSpecified).isFalse()
|
||||
|
||||
val normalRule = rules.find { it.selector.specificity < 10000 }
|
||||
assertThat(normalRule).isNotNull()
|
||||
assertThat(normalRule!!.style.spanStyle.color).isEqualTo(Color.Blue)
|
||||
assertThat(normalRule.style.blockStyle.backgroundColor.isSpecified).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_extractsFontFaceRulesAndResolvesPath() {
|
||||
val css = """
|
||||
@font-face {
|
||||
font-family: "MyCustomFont";
|
||||
src: url("../fonts/myfont.ttf");
|
||||
font-weight: bold;
|
||||
}
|
||||
p { color: black; }
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, "/some/path/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.rules.byTag).containsKey("p")
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
val fontFace = result.fontFaces.first()
|
||||
assertThat(fontFace.fontFamily).isEqualTo("mycustomfont")
|
||||
assertThat(fontFace.src).isEqualTo("/some/fonts/myfont.ttf")
|
||||
assertThat(fontFace.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
assertThat(fontFace.fontStyle).isEqualTo(FontStyle.Normal)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesFontFaceWithDataUri() {
|
||||
val dataUri = "data:font/truetype;base64,AAEAAA..."
|
||||
val css = """
|
||||
@font-face {
|
||||
font-family: 'MyDataFont';
|
||||
src: url('$dataUri');
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, "/css/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
assertThat(result.fontFaces.first().src).isEqualTo(dataUri)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_sanitizesPseudoClassesFromSelectors() {
|
||||
val css = "a:hover, p::first-line, button:focus { color: red; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.rules.byTag.keys).containsExactly("a", "p", "button")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_calculatesSpecificityCorrectly() {
|
||||
val css = """
|
||||
#myId { color: red; } /* 100 */
|
||||
p.myClass { color: green; } /* 11 */
|
||||
p { color: blue; } /* 1 */
|
||||
div p { color: yellow; } /* 2 */
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val idRule = result.rules.byId["myId"]?.first()
|
||||
val classRule = result.rules.otherComplex.find { it.selector.selector == "p.myClass" }
|
||||
val elementRule = result.rules.byTag["p"]?.first()
|
||||
val descendantRule = result.rules.otherComplex.find { it.selector.selector == "div p" }
|
||||
|
||||
assertThat(idRule?.selector?.specificity).isEqualTo(100)
|
||||
assertThat(classRule?.selector?.specificity).isEqualTo(11)
|
||||
assertThat(elementRule?.selector?.specificity).isEqualTo(1)
|
||||
assertThat(descendantRule?.selector?.specificity).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_ignoresComments() {
|
||||
val css = """
|
||||
/* This is a comment */
|
||||
p {
|
||||
color: /* another comment */ blue; /* block comment */
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val rules = result.rules.byTag["p"]
|
||||
assertThat(rules).hasSize(1)
|
||||
assertThat(rules?.first()?.style?.spanStyle?.color).isEqualTo(Color.Blue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesBorderShorthand() {
|
||||
val css = "div { border: 2px solid red; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["div"]?.first()?.style?.blockStyle
|
||||
assertThat(style?.border).isNotNull()
|
||||
assertThat(style?.border?.width).isEqualTo(2.dp)
|
||||
assertThat(style?.border?.style).isEqualTo("solid")
|
||||
assertThat(style?.border?.color).isEqualTo(Color.Red)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesMarginAndPaddingShorthand() {
|
||||
val css = "p { margin: 10px 20px; padding: 1em 2em 3em 4em; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["p"]?.first()?.style?.blockStyle
|
||||
assertThat(style?.margin?.top).isEqualTo(10.dp)
|
||||
assertThat(style?.margin?.right).isEqualTo(20.dp)
|
||||
assertThat(style?.margin?.bottom).isEqualTo(10.dp)
|
||||
assertThat(style?.margin?.left).isEqualTo(20.dp)
|
||||
|
||||
assertThat(style?.padding?.top).isEqualTo(16.dp) // 1em
|
||||
assertThat(style?.padding?.right).isEqualTo(32.dp) // 2em
|
||||
assertThat(style?.padding?.bottom).isEqualTo(48.dp) // 3em
|
||||
assertThat(style?.padding?.left).isEqualTo(64.dp) // 4em
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_handlesFontSizeWithEmUnits() {
|
||||
val css = "p { font-size: 1.2em; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["p"]?.first()?.style
|
||||
assertThat(style?.fontSize?.isEm).isTrue()
|
||||
assertThat(style?.fontSize?.value).isEqualTo(1.2f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_optimizationCategorizesRulesCorrectly() {
|
||||
val css = """
|
||||
p { color: blue; }
|
||||
.myClass { color: green; }
|
||||
#myId { color: red; }
|
||||
div > p { color: yellow; }
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.rules.byTag).containsKey("p")
|
||||
assertThat(result.rules.byClass).containsKey("myClass")
|
||||
assertThat(result.rules.byId).containsKey("myId")
|
||||
assertThat(result.rules.otherComplex).hasSize(1)
|
||||
assertThat(result.rules.otherComplex.first().selector.selector).isEqualTo("div > p")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_mediaQueryAppliesDarkThemeRules() {
|
||||
val css = """
|
||||
p { color: black; }
|
||||
@media (prefers-color-scheme: dark) {
|
||||
p { color: white; }
|
||||
}
|
||||
""".trimIndent()
|
||||
val lightResult = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(lightResult.rules.byTag["p"]?.first()?.style?.spanStyle?.color).isEqualTo(Color.Black)
|
||||
|
||||
val darkResult = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = true)
|
||||
assertThat(darkResult.rules.byTag["p"]?.last()?.style?.spanStyle?.color).isEqualTo(Color.White)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_fontFaceSelectsPreferredSourceFormat() {
|
||||
val css = """
|
||||
@font-face {
|
||||
font-family: "MyFont";
|
||||
src: url("font.woff2") format("woff2"),
|
||||
url("font.otf") format("opentype"),
|
||||
url("font.ttf") format("truetype");
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, "/css/style.css", baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
assertThat(result.fontFaces.first().src).isEqualTo("/css/font.otf")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_propertiesHandlesVariousUnitsAndValues() {
|
||||
val css = """
|
||||
p {
|
||||
font-size: 150%;
|
||||
text-transform: uppercase;
|
||||
text-decoration: underline;
|
||||
text-align: center;
|
||||
page-break-inside: avoid;
|
||||
margin: 0 auto;
|
||||
}
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["p"]?.first()?.style
|
||||
assertThat(style?.fontSize).isEqualTo(1.5.em)
|
||||
assertThat(style?.textTransform).isEqualTo("uppercase")
|
||||
assertThat(style?.spanStyle?.textDecoration).isEqualTo(TextDecoration.Underline)
|
||||
assertThat(style?.paragraphStyle?.textAlign).isEqualTo(TextAlign.Center)
|
||||
assertThat(style?.blockStyle?.pageBreakInsideAvoid).isTrue()
|
||||
assertThat(style?.blockStyle?.horizontalAlign).isEqualTo("center")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_themeAdaptationAdaptsColorsCorrectlyForDarkTheme() {
|
||||
val css = "p { color: #111; background-color: #EEE; }" // very dark text, very light bg
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = true)
|
||||
val style = result.rules.byTag["p"]?.first()?.style
|
||||
|
||||
assertThat(style?.spanStyle?.color).isEqualTo(Color.White.copy(alpha = 0.87f))
|
||||
assertThat(style?.blockStyle?.backgroundColor).isEqualTo(Color.Transparent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_dataUriWithSemicolonParsesCorrectly() {
|
||||
val dataUri = "data:font/opentype;base64,d09GMgABAAAAAAPs...;something=else"
|
||||
val css = """
|
||||
@font-face {
|
||||
font-family: 'MyDataFont';
|
||||
src: url('$dataUri');
|
||||
}
|
||||
p { color: red; }
|
||||
""".trimIndent()
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
assertThat(result.fontFaces).hasSize(1)
|
||||
assertThat(result.fontFaces.first().src).isEqualTo(dataUri)
|
||||
assertThat(result.rules.byTag).containsKey("p")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_textEmphasisParsesCorrectly() {
|
||||
val css = "p { -epub-text-emphasis-style: filled dot; -epub-text-emphasis-color: red; }"
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val emphasis = result.rules.byTag["p"]?.first()?.style?.textEmphasis
|
||||
assertThat(emphasis).isNotNull()
|
||||
assertThat(emphasis?.style).isEqualTo("dot")
|
||||
assertThat(emphasis?.fill).isEqualTo("filled")
|
||||
assertThat(emphasis?.color).isEqualTo(Color.Red)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parse_lineHeightClampsSmallEmValues() {
|
||||
val css = "p { line-height: 1.1; }" // This is treated as 1.1em
|
||||
val result = CssParser.parse(css, null, baseFontSize, density, dummyConstraints, isDarkTheme = false)
|
||||
val style = result.rules.byTag["p"]?.first()?.style
|
||||
assertThat(style?.paragraphStyle?.lineHeight).isEqualTo(2.0.em)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
// HtmlParserTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class HtmlParserTest {
|
||||
|
||||
// region Test Setup
|
||||
private val defaultTextStyle = TextStyle.Default.copy(fontSize = 16.sp, color = Color.Black)
|
||||
private val defaultDensity = Density(density = 1f, fontScale = 1f)
|
||||
private val defaultConstraints = Constraints(maxWidth = 1000)
|
||||
private val defaultChapterPath = "OEBPS/chapter1.xhtml"
|
||||
private val defaultExtractionPath = InstrumentationRegistry.getInstrumentation().targetContext.cacheDir.absolutePath + "/epub_test/"
|
||||
|
||||
private fun parse(
|
||||
html: String,
|
||||
cssRules: OptimizedCssRules? = null,
|
||||
mathSvgCache: Map<String, String> = emptyMap()
|
||||
): List<SemanticBlock> {
|
||||
val userAgentRules = CssParser.parse(
|
||||
cssContent = UserAgentStylesheet.default,
|
||||
cssPath = null,
|
||||
baseFontSizeSp = defaultTextStyle.fontSize.value,
|
||||
density = defaultDensity.density,
|
||||
constraints = defaultConstraints,
|
||||
isDarkTheme = false // This is for CSS parsing, not the semantic parser
|
||||
).rules
|
||||
|
||||
val allRules = cssRules?.let { userAgentRules.merge(it) } ?: userAgentRules
|
||||
|
||||
return htmlToSemanticBlocks(
|
||||
html = "<body>$html</body>", // Wrap in body to match real usage
|
||||
cssRules = allRules, // Use the combined list of rules
|
||||
textStyle = defaultTextStyle,
|
||||
chapterAbsPath = defaultChapterPath,
|
||||
extractionBasePath = defaultExtractionPath,
|
||||
density = defaultDensity,
|
||||
fontFamilyMap = emptyMap(),
|
||||
constraints = defaultConstraints,
|
||||
mathSvgCache = mathSvgCache
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_simpleParagraphTag_createsSemanticParagraph() {
|
||||
val blocks = parse("<p>Hello World</p>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticParagraph::class.java)
|
||||
val pBlock = block as SemanticParagraph
|
||||
assertThat(pBlock.text).isEqualTo("Hello World")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_headerTag_createsSemanticHeaderWithCorrectLevel() {
|
||||
val blocks = parse("<h2>Chapter 2</h2>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticHeader::class.java)
|
||||
val hBlock = block as SemanticHeader
|
||||
assertThat(hBlock.text).isEqualTo("Chapter 2")
|
||||
assertThat(hBlock.level).isEqualTo(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_nestedTag_inheritsStyleFromParent() {
|
||||
val blocks = parse("<div style=\"color: #FF0000;\"><p>This text should be red.</p></div>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks.first() as SemanticParagraph
|
||||
assertThat(pBlock.text).isEqualTo("This text should be red.")
|
||||
assertThat(pBlock.style.spanStyle.color).isEqualTo(Color.Red)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_inlineStyle_overridesCssRule() {
|
||||
val css = "p { color: red; }"
|
||||
val cssRules = CssParser.parse(css, null, 16f, 1f, defaultConstraints, isDarkTheme = false).rules
|
||||
|
||||
val blocks = parse("<p style=\"color: green;\">I am green.</p>", cssRules = cssRules)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks.first() as SemanticParagraph
|
||||
val blockStyle = pBlock.style.spanStyle
|
||||
assertThat(blockStyle.color).isEqualTo(Color.Green)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_elementWithDisplayNone_isNotIncludedInOutput() {
|
||||
val blocks = parse("<p>Visible</p><p style=\"display: none;\">Invisible</p>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
assertThat((blocks.first() as SemanticParagraph).text).isEqualTo("Visible")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_imageWithNonExistentPath_producesNoBlock() {
|
||||
// This tests the negative path where resolveImagePath returns null
|
||||
val blocks = parse("<img src=\"non/existent/path.jpg\" />")
|
||||
|
||||
assertThat(blocks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_unorderedList_createsSemanticList() {
|
||||
val blocks = parse("<ul><li>Item 1</li><li>Item 2</li></ul>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val listBlock = blocks.first() as SemanticList
|
||||
assertThat(listBlock.isOrdered).isFalse()
|
||||
assertThat(listBlock.items).hasSize(2)
|
||||
|
||||
val item1 = listBlock.items[0]
|
||||
val item2 = listBlock.items[1]
|
||||
|
||||
assertThat(item1.text).isEqualTo("Item 1")
|
||||
assertThat(item2.text).isEqualTo("Item 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_orderedListWithCssType_createsCorrectSemanticList() {
|
||||
val css = "ol { list-style-type: lower-roman; }"
|
||||
val cssRules = CssParser.parse(css, null, 16f, 1f, defaultConstraints, isDarkTheme = false).rules
|
||||
|
||||
val blocks = parse("<ol><li>Item 1</li><li>Item 2</li></ol>", cssRules = cssRules)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val listBlock = blocks.first() as SemanticList
|
||||
assertThat(listBlock.isOrdered).isTrue()
|
||||
assertThat(listBlock.style.blockStyle.listStyleType).isEqualTo("lower-roman")
|
||||
assertThat(listBlock.items).hasSize(2)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_table_createsSemanticTableWithCorrectStructure() {
|
||||
val html = """
|
||||
<table>
|
||||
<tr>
|
||||
<th>Header 1</th>
|
||||
<th style="text-align: right;">Header 2</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Data A</td>
|
||||
<td>Data B</td>
|
||||
</tr>
|
||||
</table>
|
||||
""".trimIndent()
|
||||
|
||||
val blocks = parse(html)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val tableBlock = blocks.first() as SemanticTable
|
||||
assertThat(tableBlock.rows).hasSize(2)
|
||||
|
||||
// Verify Header Row
|
||||
val headerRow = tableBlock.rows[0]
|
||||
assertThat(headerRow).hasSize(2)
|
||||
assertThat(headerRow[0].isHeader).isTrue()
|
||||
assertThat((headerRow[0].content.first() as SemanticParagraph).text).isEqualTo("Header 1")
|
||||
assertThat(headerRow[1].isHeader).isTrue()
|
||||
assertThat((headerRow[1].content.first() as SemanticParagraph).text).isEqualTo("Header 2")
|
||||
assertThat(headerRow[1].style.paragraphStyle.textAlign).isEqualTo(TextAlign.End)
|
||||
|
||||
// Verify Data Row
|
||||
val dataRow = tableBlock.rows[1]
|
||||
assertThat(dataRow).hasSize(2)
|
||||
assertThat(dataRow[0].isHeader).isFalse()
|
||||
assertThat((dataRow[0].content.first() as SemanticParagraph).text).isEqualTo("Data A")
|
||||
assertThat(dataRow[1].isHeader).isFalse()
|
||||
assertThat((dataRow[1].content.first() as SemanticParagraph).text).isEqualTo("Data B")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_textTransformations_areAppliedCorrectly() {
|
||||
val blocks = parse("<p style=\"text-transform: uppercase;\">hello world</p>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks.first() as SemanticParagraph
|
||||
// The transformation is applied during text building
|
||||
assertThat(pBlock.text).isEqualTo("HELLO WORLD")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_complexInlineFormatting_isPreserved() {
|
||||
val html = "<p>This is <b>bold</b> and <i>italic</i> text.</p>"
|
||||
val blocks = parse(html)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks.first() as SemanticParagraph
|
||||
|
||||
assertThat(pBlock.text).isEqualTo("This is bold and italic text.")
|
||||
|
||||
// Find the range for "bold" and check its style
|
||||
val boldRange = pBlock.spans.find { pBlock.text.substring(it.start, it.end) == "bold" }
|
||||
assertThat(boldRange).isNotNull()
|
||||
assertThat(boldRange!!.style.spanStyle.fontWeight).isEqualTo(FontWeight.Bold)
|
||||
|
||||
// Find the range for "italic" and check its style
|
||||
val italicRange =
|
||||
pBlock.spans.find { pBlock.text.substring(it.start, it.end) == "italic" }
|
||||
assertThat(italicRange).isNotNull()
|
||||
assertThat(italicRange!!.style.spanStyle.fontStyle).isEqualTo(androidx.compose.ui.text.font.FontStyle.Italic)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_imageWithExistingPath_createsSemanticImageWithCorrectPath() {
|
||||
// SETUP
|
||||
val imageRelativeSrc = "../images/test.jpg"
|
||||
val chapterParentDir = File(defaultChapterPath).parent ?: ""
|
||||
val imageFile = File(File(defaultExtractionPath, chapterParentDir), imageRelativeSrc).canonicalFile
|
||||
imageFile.parentFile?.mkdirs()
|
||||
imageFile.createNewFile()
|
||||
imageFile.deleteOnExit()
|
||||
|
||||
// ACTION
|
||||
val blocks = parse("<img src=\"$imageRelativeSrc\" alt=\"A test image\" />")
|
||||
|
||||
// ASSERT
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticImage::class.java)
|
||||
|
||||
val imageBlock = block as SemanticImage
|
||||
assertThat(imageBlock.path).isEqualTo(imageFile.absolutePath)
|
||||
assertThat(imageBlock.altText).isEqualTo("A test image")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_pseudoElements_areIgnoredByTheParser() {
|
||||
val css = "p::before { content: \"Note: \"; }"
|
||||
val cssRules = CssParser.parse(css, null, 16f, 1f, defaultConstraints, isDarkTheme = false).rules
|
||||
val blocks = parse("<p>This is a test.</p>", cssRules = cssRules)
|
||||
|
||||
// The parser now ignores pseudo-elements, so only the paragraph content should be parsed.
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks[0] as SemanticParagraph
|
||||
assertThat(pBlock.text).isEqualTo("This is a test.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_hrWithPseudoElement_ignoresPseudoElement() {
|
||||
val css = "hr.fancy::after { content: ''; display: block; border-bottom: 2px solid blue; }"
|
||||
val cssRules = CssParser.parse(css, null, 16f, 1f, defaultConstraints, isDarkTheme = false).rules
|
||||
val blocks = parse("<hr class=\"fancy\" />", cssRules = cssRules)
|
||||
|
||||
// The pseudo-element is ignored, so only the spacer from <hr> is created.
|
||||
assertThat(blocks).hasSize(1)
|
||||
assertThat(blocks[0]).isInstanceOf(SemanticSpacer::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_inlineSvg_createsSemanticMathWithCorrectContent() {
|
||||
val svg = """
|
||||
<svg width="100" height="100">
|
||||
<title>My SVG</title>
|
||||
<circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
|
||||
<text x="50" y="50" fill="red">Hello</text>
|
||||
</svg>
|
||||
""".trimIndent()
|
||||
val blocks = parse(svg)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticMath::class.java)
|
||||
|
||||
val mathBlock = block as SemanticMath
|
||||
assertThat(mathBlock.altText).isEqualTo("My SVG")
|
||||
// The parser now passes the SVG content through as-is.
|
||||
assertThat(mathBlock.svgContent).contains("""<text x="50" y="50" fill="red">Hello</text>""")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_imgTagWithSvgSource_createsSemanticMath() {
|
||||
// SETUP
|
||||
val svgContent = """<svg width="10" height="10"><rect width="10" height="10" /></svg>"""
|
||||
val svgRelativeSrc = "images/test.svg"
|
||||
val chapterParentDir = File(defaultChapterPath).parent ?: ""
|
||||
val svgFile = File(File(defaultExtractionPath, chapterParentDir), svgRelativeSrc).canonicalFile
|
||||
svgFile.parentFile?.mkdirs()
|
||||
svgFile.writeText(svgContent)
|
||||
svgFile.deleteOnExit()
|
||||
|
||||
// ACTION
|
||||
val blocks = parse("<img src=\"$svgRelativeSrc\" />")
|
||||
|
||||
// ASSERT
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticMath::class.java)
|
||||
val mathBlock = block as SemanticMath
|
||||
assertThat(mathBlock.svgContent).contains("<rect")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_mathPlaceholder_createsSemanticMathFromCache() {
|
||||
val svgContent = "<svg><text>E=mc^2</text></svg>"
|
||||
val cache = mapOf("math-123" to svgContent)
|
||||
val blocks = parse(
|
||||
html = """<math-placeholder id="math-123" alttext="An equation"></math-placeholder>""",
|
||||
mathSvgCache = cache
|
||||
)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first() as SemanticMath
|
||||
assertThat(block.svgContent).isEqualTo(svgContent)
|
||||
assertThat(block.altText).isEqualTo("An equation")
|
||||
assertThat(block.isFromMathJax).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_displayFlex_createsSemanticFlexContainer() {
|
||||
val html = """
|
||||
<div style="display: flex;">
|
||||
<p>One</p>
|
||||
<p>Two</p>
|
||||
</div>
|
||||
""".trimIndent()
|
||||
val blocks = parse(html)
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticFlexContainer::class.java)
|
||||
|
||||
val flexBlock = block as SemanticFlexContainer
|
||||
assertThat(flexBlock.children).hasSize(2)
|
||||
assertThat(flexBlock.children[0]).isInstanceOf(SemanticParagraph::class.java)
|
||||
assertThat((flexBlock.children[0] as SemanticParagraph).text).isEqualTo("One")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_brTagInParagraph_createsNewlineCharacter() {
|
||||
val blocks = parse("<p>Line one.<br>Line two.</p>")
|
||||
|
||||
assertThat(blocks).hasSize(1)
|
||||
val pBlock = blocks.first() as SemanticParagraph
|
||||
assertThat(pBlock.text).isEqualTo("Line one.\nLine two.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun htmlToSemanticBlocks_imageWithRootRelativePath_resolvesCorrectly() {
|
||||
// SETUP
|
||||
val imageRootRelativeSrc = "images/test.jpg"
|
||||
val imageFile = File(defaultExtractionPath, imageRootRelativeSrc).canonicalFile
|
||||
imageFile.parentFile?.mkdirs()
|
||||
imageFile.createNewFile()
|
||||
imageFile.deleteOnExit()
|
||||
|
||||
// ACTION
|
||||
val blocks = parse("<img src=\"$imageRootRelativeSrc\" alt=\"A test image\" />")
|
||||
|
||||
// ASSERT
|
||||
assertThat(blocks).hasSize(1)
|
||||
val block = blocks.first()
|
||||
assertThat(block).isInstanceOf(SemanticImage::class.java)
|
||||
|
||||
val imageBlock = block as SemanticImage
|
||||
assertThat(imageBlock.path).isEqualTo(imageFile.absolutePath)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// MainDispatcherRule.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
/**
|
||||
* A JUnit TestRule that sets the Main dispatcher to a TestDispatcher for the duration of a test.
|
||||
* This allows tests to execute coroutines on the Main dispatcher without needing a real Android environment.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MainDispatcherRule(
|
||||
val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
) : TestRule {
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
@Throws(Throwable::class)
|
||||
override fun evaluate() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
try {
|
||||
base.evaluate()
|
||||
} finally {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
// PaginatedReaderDataTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.ParagraphStyle
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PaginatedReaderDataTest {
|
||||
|
||||
@Test
|
||||
fun cssStyle_mergeCorrectlyCombinesStyles() {
|
||||
val baseStyle = CssStyle(
|
||||
spanStyle = SpanStyle(color = Color.Black, fontWeight = FontWeight.Normal, fontSize = 16.sp),
|
||||
paragraphStyle = ParagraphStyle(textAlign = TextAlign.Start),
|
||||
fontFamilies = listOf("serif"),
|
||||
display = "block"
|
||||
)
|
||||
|
||||
val overrideStyle = CssStyle(
|
||||
spanStyle = SpanStyle(color = Color.Red, fontStyle = FontStyle.Italic),
|
||||
paragraphStyle = ParagraphStyle(textAlign = TextAlign.Center),
|
||||
fontFamilies = listOf("sans-serif"),
|
||||
textTransform = "uppercase"
|
||||
)
|
||||
|
||||
val merged = baseStyle.merge(overrideStyle)
|
||||
|
||||
// Overridden properties
|
||||
assertThat(merged.spanStyle.color).isEqualTo(Color.Red)
|
||||
assertThat(merged.spanStyle.fontStyle).isEqualTo(FontStyle.Italic)
|
||||
assertThat(merged.paragraphStyle.textAlign).isEqualTo(TextAlign.Center)
|
||||
assertThat(merged.fontFamilies).containsExactly("sans-serif")
|
||||
assertThat(merged.textTransform).isEqualTo("uppercase")
|
||||
|
||||
// Inherited properties
|
||||
assertThat(merged.spanStyle.fontWeight).isEqualTo(FontWeight.Normal)
|
||||
assertThat(merged.spanStyle.fontSize).isEqualTo(16.sp)
|
||||
assertThat(merged.display).isEqualTo("block")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cssStyle_mergeWithEmptyOverrideDoesNotChangeBase() {
|
||||
val baseStyle = CssStyle(
|
||||
spanStyle = SpanStyle(color = Color.Black, fontWeight = FontWeight.Normal),
|
||||
fontFamilies = listOf("serif")
|
||||
)
|
||||
val overrideStyle = CssStyle()
|
||||
|
||||
val merged = baseStyle.merge(overrideStyle)
|
||||
|
||||
assertThat(merged).isEqualTo(baseStyle)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blockStyle_mergeUsesOverrideProperties() {
|
||||
val baseStyle = BlockStyle(
|
||||
padding = BoxBorders(top = 10.dp, left = 10.dp),
|
||||
margin = BoxBorders(top = 5.dp, bottom = 5.dp),
|
||||
width = 100.dp,
|
||||
backgroundColor = Color.White
|
||||
)
|
||||
|
||||
val overrideStyle = BlockStyle(
|
||||
padding = BoxBorders(top = 5.dp, right = 5.dp),
|
||||
margin = BoxBorders(bottom = 10.dp, left = 10.dp),
|
||||
width = 200.dp,
|
||||
backgroundColor = Color.Black,
|
||||
border = BorderStyle(width = 1.dp, color = Color.Red)
|
||||
)
|
||||
|
||||
val merged = baseStyle.merge(overrideStyle)
|
||||
|
||||
// Padding should be from override, not additive
|
||||
assertThat(merged.padding.top).isEqualTo(5.dp)
|
||||
assertThat(merged.padding.left).isEqualTo(10.dp) // from base
|
||||
assertThat(merged.padding.right).isEqualTo(5.dp)
|
||||
assertThat(merged.padding.bottom).isEqualTo(0.dp) // from base
|
||||
|
||||
// Margin should be from override
|
||||
assertThat(merged.margin.top).isEqualTo(5.dp) // from base
|
||||
assertThat(merged.margin.bottom).isEqualTo(10.dp)
|
||||
assertThat(merged.margin.left).isEqualTo(10.dp)
|
||||
assertThat(merged.margin.right).isEqualTo(0.dp) // from base
|
||||
|
||||
// Other properties
|
||||
assertThat(merged.width).isEqualTo(200.dp)
|
||||
assertThat(merged.backgroundColor).isEqualTo(Color.Black)
|
||||
assertThat(merged.border).isNotNull()
|
||||
assertThat(merged.border?.width).isEqualTo(1.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun blockStyle_mergeWithEmptyOverrideDoesNotChangeBase() {
|
||||
val baseStyle = BlockStyle(
|
||||
padding = BoxBorders(10.dp, 10.dp, 10.dp, 10.dp),
|
||||
margin = BoxBorders(5.dp, 5.dp, 5.dp, 5.dp),
|
||||
width = 100.dp,
|
||||
backgroundColor = Color.White
|
||||
)
|
||||
val overrideStyle = BlockStyle()
|
||||
|
||||
val merged = baseStyle.merge(overrideStyle)
|
||||
|
||||
assertThat(merged.padding.top).isEqualTo(10.dp)
|
||||
assertThat(merged.margin.top).isEqualTo(5.dp)
|
||||
assertThat(merged.width).isEqualTo(100.dp)
|
||||
assertThat(merged.backgroundColor).isEqualTo(Color.White)
|
||||
assertThat(merged.border).isNull()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,304 @@
|
|||
// PaginatedReaderViewModelTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshots.Snapshot
|
||||
import androidx.compose.ui.text.TextMeasurer
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.aryan.reader.SearchResult
|
||||
import com.aryan.reader.epub.EpubBook
|
||||
import com.aryan.reader.epub.EpubChapter
|
||||
import com.aryan.reader.paginatedreader.data.BookCacheDao
|
||||
import com.aryan.reader.paginatedreader.data.BookCacheDatabase
|
||||
import com.aryan.reader.paginatedreader.data.BookProcessingWorker
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkAll
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.test.advanceUntilIdle
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private class FakePaginator(
|
||||
initiallyLoading: Boolean,
|
||||
initialPageCount: Int,
|
||||
initialGeneration: Int
|
||||
) : IPaginator {
|
||||
override var isLoading by mutableStateOf(initiallyLoading)
|
||||
override var totalPageCount by mutableIntStateOf(initialPageCount)
|
||||
override var generation by mutableIntStateOf(initialGeneration)
|
||||
override val pageShiftRequest: Flow<Int> = emptyFlow()
|
||||
|
||||
var lastNavigatedHref: String? = null
|
||||
var lastNavigatedChapter: String? = null
|
||||
|
||||
override fun getPageContent(pageIndex: Int): Page? = null
|
||||
override fun getChapterPathForPage(pageIndex: Int): String? = null
|
||||
override fun getPlainTextForChapter(chapterIndex: Int): String? = null
|
||||
|
||||
override fun navigateToHref(
|
||||
currentChapterAbsPath: String,
|
||||
href: String,
|
||||
onNavigationComplete: (pageIndex: Int) -> Unit
|
||||
) {
|
||||
lastNavigatedChapter = currentChapterAbsPath
|
||||
lastNavigatedHref = href
|
||||
}
|
||||
|
||||
override fun findPageForSearchResult(
|
||||
result: SearchResult,
|
||||
onResult: (Int) -> Unit
|
||||
) = Unit
|
||||
|
||||
// Add stubs for the other missing interface members
|
||||
override fun findPageForCfi(chapterIndex: Int, cfi: String, onResult: (Int) -> Unit) = Unit
|
||||
override fun findPageForCfiAndOffset(
|
||||
chapterIndex: Int,
|
||||
cfi: String,
|
||||
charOffset: Int
|
||||
): Int? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun findChapterIndexForPage(pageIndex: Int): Int? = null
|
||||
override fun getCfiForPage(pageIndex: Int): String? = null
|
||||
override fun onUserScrolledTo(pageIndex: Int) = Unit
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PaginatedReaderViewModelTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private lateinit var viewModel: PaginatedReaderViewModel
|
||||
private lateinit var fakePaginator: FakePaginator
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
viewModel = PaginatedReaderViewModel()
|
||||
fakePaginator = FakePaginator(
|
||||
initiallyLoading = true,
|
||||
initialPageCount = 0,
|
||||
initialGeneration = 0
|
||||
)
|
||||
viewModel.setPaginatorForTest(fakePaginator)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uiState_reflectsPaginatorInitialState() = runTest {
|
||||
val initialState = viewModel.uiState.value
|
||||
assertThat(initialState.isLoading).isTrue()
|
||||
assertThat(initialState.totalPageCount).isEqualTo(0)
|
||||
assertThat(initialState.generation).isEqualTo(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uiState_updatesWhenPaginatorIsLoadingChanges() = runTest {
|
||||
assertThat(viewModel.uiState.value.isLoading).isTrue()
|
||||
|
||||
fakePaginator.isLoading = false
|
||||
Snapshot.sendApplyNotifications()
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.uiState.value.isLoading).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uiState_updatesWhenPaginatorTotalPageCountChanges() = runTest {
|
||||
assertThat(viewModel.uiState.value.totalPageCount).isEqualTo(0)
|
||||
|
||||
fakePaginator.totalPageCount = 123
|
||||
Snapshot.sendApplyNotifications()
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.uiState.value.totalPageCount).isEqualTo(123)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uiState_updatesWhenPaginatorGenerationChanges() = runTest {
|
||||
assertThat(viewModel.uiState.value.generation).isEqualTo(0)
|
||||
|
||||
fakePaginator.generation = 5
|
||||
Snapshot.sendApplyNotifications()
|
||||
advanceUntilIdle()
|
||||
|
||||
assertThat(viewModel.uiState.value.generation).isEqualTo(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onLinkClick_callsPaginatorNavigateToHrefWithCorrectArguments() {
|
||||
val currentChapter = "chapter1.xhtml"
|
||||
val href = "#section2"
|
||||
|
||||
viewModel.onLinkClick(currentChapter, href) {}
|
||||
|
||||
assertThat(fakePaginator.lastNavigatedChapter).isEqualTo(currentChapter)
|
||||
assertThat(fakePaginator.lastNavigatedHref).isEqualTo(href)
|
||||
}
|
||||
@Test
|
||||
fun initialize_createsARealPaginatorAndUpdateState() = runTest {
|
||||
// Arrange
|
||||
val viewModel = PaginatedReaderViewModel() // Create a fresh ViewModel
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
val textMeasurer = mockk<TextMeasurer>(relaxed = true)
|
||||
val constraints = Constraints(maxWidth = 1080, maxHeight = 1920)
|
||||
val textStyle = TextStyle.Default
|
||||
val density = Density(1f)
|
||||
val mathMLRenderer = mockk<MathMLRenderer>(relaxed = true)
|
||||
val testBook = EpubBook(
|
||||
fileName = "test.epub",
|
||||
title = "Test Book",
|
||||
author = "Test Author",
|
||||
language = "en",
|
||||
coverImage = null,
|
||||
chapters = listOf(
|
||||
EpubChapter(
|
||||
chapterId = "ch1",
|
||||
title = "Chapter 1",
|
||||
htmlFilePath = "ch1.html",
|
||||
absPath = "/ops/ch1.html",
|
||||
htmlContent = "<p>Some content</p>",
|
||||
plainTextContent = "Some content"
|
||||
)
|
||||
),
|
||||
css = mapOf("/ops/style.css" to "p {color: red;}"),
|
||||
extractionBasePath = ""
|
||||
)
|
||||
|
||||
// Mock dependencies for BookPaginator
|
||||
val mockDao = mockk<BookCacheDao>(relaxed = true)
|
||||
coEvery { mockDao.getProcessedBook(any()) } returns null // Simulate cache miss
|
||||
|
||||
val mockDb = mockk<BookCacheDatabase>()
|
||||
every { mockDb.bookCacheDao() } returns mockDao
|
||||
|
||||
mockkObject(BookCacheDatabase.Companion)
|
||||
every { BookCacheDatabase.getDatabase(any()) } returns mockDb
|
||||
|
||||
mockkObject(BookProcessingWorker.Companion)
|
||||
every { BookProcessingWorker.enqueue(any(), any(), any(), any(), any(), any()) } returns Unit
|
||||
|
||||
// Pre-condition check
|
||||
assertThat(viewModel.uiState.value.isLoading).isTrue()
|
||||
assertThat(viewModel.paginator).isNull()
|
||||
|
||||
// Act
|
||||
viewModel.initialize(
|
||||
book = testBook,
|
||||
textMeasurer = textMeasurer,
|
||||
textConstraints = constraints,
|
||||
textStyle = textStyle,
|
||||
density = density,
|
||||
isDarkTheme = false,
|
||||
context = context,
|
||||
initialChapterToPaginate = 0,
|
||||
mathMLRenderer = mathMLRenderer
|
||||
)
|
||||
advanceUntilIdle() // Allow coroutines to complete
|
||||
|
||||
// Assert
|
||||
assertThat(viewModel.paginator).isInstanceOf(BookPaginator::class.java)
|
||||
assertThat(viewModel.uiState.value.isLoading).isFalse()
|
||||
assertThat(viewModel.uiState.value.totalPageCount).isGreaterThan(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_isIdempotent() = runTest {
|
||||
// Arrange
|
||||
val viewModel = PaginatedReaderViewModel()
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
val textMeasurer = mockk<TextMeasurer>(relaxed = true)
|
||||
val constraints = Constraints(maxWidth = 1080, maxHeight = 1920)
|
||||
val textStyle = TextStyle.Default
|
||||
val density = Density(1f)
|
||||
val mathMLRenderer = mockk<MathMLRenderer>(relaxed = true)
|
||||
val testBook = EpubBook(
|
||||
fileName = "test.epub",
|
||||
title = "Test Book",
|
||||
author = "Test Author",
|
||||
language = "en",
|
||||
coverImage = null,
|
||||
chapters = listOf(
|
||||
EpubChapter(
|
||||
chapterId = "ch1",
|
||||
title = "Chapter 1",
|
||||
htmlFilePath = "ch1.html",
|
||||
absPath = "/ops/ch1.html",
|
||||
htmlContent = "<p>Some content</p>",
|
||||
plainTextContent = "Some content"
|
||||
)
|
||||
),
|
||||
css = mapOf("/ops/style.css" to "p {color: red;}"),
|
||||
extractionBasePath = ""
|
||||
)
|
||||
|
||||
// Mock dependencies
|
||||
val mockDao = mockk<BookCacheDao>(relaxed = true)
|
||||
coEvery { mockDao.getProcessedBook(any()) } returns null
|
||||
val mockDb = mockk<BookCacheDatabase>()
|
||||
every { mockDb.bookCacheDao() } returns mockDao
|
||||
mockkObject(BookCacheDatabase.Companion)
|
||||
every { BookCacheDatabase.getDatabase(any()) } returns mockDb
|
||||
mockkObject(BookProcessingWorker.Companion)
|
||||
every { BookProcessingWorker.enqueue(any(), any(), any(), any(), any(), any()) } returns Unit
|
||||
|
||||
// Act
|
||||
viewModel.initialize(
|
||||
book = testBook,
|
||||
textMeasurer = textMeasurer,
|
||||
textConstraints = constraints,
|
||||
textStyle = textStyle,
|
||||
density = density,
|
||||
isDarkTheme = false,
|
||||
context = context,
|
||||
initialChapterToPaginate = 0,
|
||||
mathMLRenderer = mathMLRenderer
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
val firstPaginator = viewModel.paginator
|
||||
assertThat(firstPaginator).isNotNull()
|
||||
|
||||
// Act again
|
||||
viewModel.initialize(
|
||||
book = testBook,
|
||||
textMeasurer = textMeasurer,
|
||||
textConstraints = constraints,
|
||||
textStyle = textStyle,
|
||||
density = density,
|
||||
isDarkTheme = false,
|
||||
context = context,
|
||||
initialChapterToPaginate = 0,
|
||||
mathMLRenderer = mathMLRenderer
|
||||
)
|
||||
advanceUntilIdle()
|
||||
|
||||
// Assert
|
||||
val secondPaginator = viewModel.paginator
|
||||
assertThat(secondPaginator).isSameInstanceAs(firstPaginator)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,278 @@
|
|||
// PaginatorTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
class FakeSplittableMeasurementProvider(
|
||||
private val heights: Map<ContentBlock, Int>,
|
||||
private val splittableParagraphs: Map<ParagraphBlock, Pair<ParagraphBlock, ParagraphBlock>> = emptyMap(),
|
||||
private val splittableWrappers: Map<WrappingContentBlock, Pair<WrappingContentBlock, List<ContentBlock>>> = emptyMap()
|
||||
) : BlockMeasurementProvider {
|
||||
override suspend fun measure(block: ContentBlock): Int {
|
||||
// Provide a more helpful error message if a block's height is not defined.
|
||||
return heights[block] ?: error("No height specified for block: $block")
|
||||
}
|
||||
|
||||
override suspend fun split(block: ParagraphBlock, availableHeight: Int): Pair<ParagraphBlock, ParagraphBlock>? {
|
||||
val splitPair = splittableParagraphs[block]
|
||||
if (splitPair != null) {
|
||||
val part1Height = heights[splitPair.first] ?: 0
|
||||
// Only return the split pair if the first part actually fits in the available height.
|
||||
if (part1Height <= availableHeight) {
|
||||
return splitPair
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun split(block: WrappingContentBlock, availableHeight: Int): Pair<WrappingContentBlock, List<ContentBlock>>? {
|
||||
val splitPair = splittableWrappers[block]
|
||||
if (splitPair != null) {
|
||||
val part1Height = heights[splitPair.first] ?: 0
|
||||
if (part1Height <= availableHeight) {
|
||||
return splitPair
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PaginatorTest {
|
||||
|
||||
private val testDensity = Density(density = 1f, fontScale = 1f)
|
||||
private val pageHeight = 1000
|
||||
|
||||
@Test
|
||||
fun paginate_givenEmptyBlocks_createsZeroPages() = runTest {
|
||||
val pages = paginate(emptyList(), pageHeight, FakeSplittableMeasurementProvider(emptyMap()), testDensity)
|
||||
assertThat(pages).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_givenBlocksThatFit_createsOnePage() = runTest {
|
||||
val block1 = ParagraphBlock(content = AnnotatedString("Block 1"), blockIndex = 0)
|
||||
val block2 = ParagraphBlock(content = AnnotatedString("Block 2"), blockIndex = 1)
|
||||
val blocks = listOf(block1, block2)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(block1 to 200, block2 to 300)
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(1)
|
||||
assertThat(pages.first().content).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_givenBlockThatOverflows_createsTwoPages() = runTest {
|
||||
val block1 = ParagraphBlock(content = AnnotatedString("Block 1"), blockIndex = 0) // Height: 600
|
||||
val block2 = ParagraphBlock(content = AnnotatedString("Block 2"), blockIndex = 1) // Height: 500
|
||||
val blocks = listOf(block1, block2)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(block1 to 600, block2 to 500)
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(2)
|
||||
assertThat(pages[0].content).containsExactly(block1)
|
||||
assertThat(pages[1].content).containsExactly(block2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_correctlySplitsAParagraphBlock() = runTest {
|
||||
val block1 = ParagraphBlock(content = AnnotatedString("First block"), blockIndex = 0)
|
||||
val originalParagraph = ParagraphBlock(content = AnnotatedString("Long text to be split"), blockIndex = 1)
|
||||
val part1 = ParagraphBlock(content = AnnotatedString("Long text"), blockIndex = 1)
|
||||
val part2 = ParagraphBlock(content = AnnotatedString("to be split"), blockIndex = 1)
|
||||
val blocks = listOf(block1, originalParagraph)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(
|
||||
block1 to 500,
|
||||
originalParagraph to 800,
|
||||
part1 to 450, // Fits in the remaining 500
|
||||
part2 to 350
|
||||
),
|
||||
splittableParagraphs = mapOf(originalParagraph to (part1 to part2))
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(2)
|
||||
assertThat(pages[0].content).containsExactly(block1, part1).inOrder()
|
||||
assertThat(pages[1].content).containsExactly(part2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_correctlySplitsAWrappingContentBlock() = runTest {
|
||||
val image = ImageBlock("image.png", null, 100f, 300f, blockIndex = 0)
|
||||
val para1 = ParagraphBlock(content = AnnotatedString("Para 1"), blockIndex = 1)
|
||||
val para2 = ParagraphBlock(content = AnnotatedString("Para 2"), blockIndex = 2)
|
||||
val originalWrapper = WrappingContentBlock(floatedImage = image, paragraphsToWrap = listOf(para1, para2), blockIndex = 3)
|
||||
|
||||
val splitWrapper = WrappingContentBlock(floatedImage = image, paragraphsToWrap = listOf(para1), blockIndex = 3)
|
||||
val remainingBlocks = listOf(para2)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(
|
||||
originalWrapper to 1500,
|
||||
splitWrapper to 300,
|
||||
para2 to 200
|
||||
),
|
||||
splittableWrappers = mapOf(originalWrapper to (splitWrapper to remainingBlocks))
|
||||
)
|
||||
|
||||
val pages = paginate(listOf(originalWrapper), pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(2)
|
||||
assertThat(pages[0].content).containsExactly(splitWrapper)
|
||||
assertThat(pages[1].content).containsExactly(para2)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun paginate_respectsPageBreakInsideAvoid() = runTest {
|
||||
val block1 = ParagraphBlock(content = AnnotatedString("First block"), blockIndex = 0) // Height 800
|
||||
val unsplittableBlock = ParagraphBlock(
|
||||
content = AnnotatedString("Can't split me"),
|
||||
style = BlockStyle(pageBreakInsideAvoid = true),
|
||||
blockIndex = 1
|
||||
) // Height 300
|
||||
val blocks = listOf(block1, unsplittableBlock)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(block1 to 800, unsplittableBlock to 300)
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(2)
|
||||
assertThat(pages[0].content).containsExactly(block1)
|
||||
assertThat(pages[1].content).containsExactly(unsplittableBlock)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_oversizedUnsplittableBlockGetsItsOwnPage() = runTest {
|
||||
val oversizedBlock = ImageBlock(path = "test.jpg", altText = null, blockIndex = 0) // Height 1200
|
||||
val blocks = listOf(oversizedBlock)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(heights = mapOf(oversizedBlock to 1200))
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(1)
|
||||
assertThat(pages[0].content).containsExactly(oversizedBlock)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_collapsesVerticalMarginsBetweenBlocks() = runTest {
|
||||
val block1 = ParagraphBlock(
|
||||
content = AnnotatedString("Block 1"),
|
||||
style = BlockStyle(margin = BoxBorders(bottom = 50.dp)), // 50px margin
|
||||
blockIndex = 0
|
||||
)
|
||||
val block2 = ParagraphBlock(
|
||||
content = AnnotatedString("Block 2"),
|
||||
style = BlockStyle(margin = BoxBorders(top = 80.dp)), // 80px margin
|
||||
blockIndex = 1
|
||||
)
|
||||
val blocks = listOf(block1, block2)
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(block1 to 100, block2 to 100)
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
|
||||
assertThat(pages).hasSize(1)
|
||||
val pageContent = pages.first().content
|
||||
assertThat(pageContent).hasSize(2)
|
||||
// The paginator logic sets the bottom margin of the previous block to 0
|
||||
// and sets the top margin of the current block to the collapsed value.
|
||||
assertThat(pageContent[0].style.margin.bottom).isEqualTo(0.dp)
|
||||
assertThat(pageContent[1].style.margin.top).isEqualTo(80.dp) // max(50, 80) is 80
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_preservesTopMarginOfTheFirstBlockOnANewPage() = runTest {
|
||||
val block1 = ParagraphBlock(
|
||||
content = AnnotatedString("Block 1"),
|
||||
style = BlockStyle(margin = BoxBorders(top = 30.dp)),
|
||||
blockIndex = 0
|
||||
)
|
||||
val block2 = ParagraphBlock(content = AnnotatedString("Block 2"), blockIndex = 1)
|
||||
val blocks = listOf(block1, block2)
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(block1 to 980, block2 to 100)
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
assertThat(pages).hasSize(2)
|
||||
|
||||
// First block on page 1 should have its top margin preserved.
|
||||
val page1Block1 = pages[0].content.first()
|
||||
assertThat(page1Block1.style.margin.top).isEqualTo(30.dp)
|
||||
|
||||
// First block on page 2 should also have its top margin preserved.
|
||||
val page2Block1 = pages[1].content.first()
|
||||
assertThat(page2Block1.style.margin.top).isEqualTo(0.dp) // The default is 0.dp
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_blockPushedToNextPageWhenNotEnoughSpaceForSplitting() = runTest {
|
||||
val block1 = ParagraphBlock(content = AnnotatedString("Block 1"), blockIndex = 0)
|
||||
val splittableBlock = ParagraphBlock(content = AnnotatedString("Splittable"), blockIndex = 1)
|
||||
val part1 = ParagraphBlock(content = AnnotatedString("Split"), blockIndex = 1)
|
||||
val part2 = ParagraphBlock(content = AnnotatedString("table"), blockIndex = 1)
|
||||
val blocks = listOf(block1, splittableBlock)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(
|
||||
block1 to 960, // Leaves 40px remaining, which is < 50, so no split should occur
|
||||
splittableBlock to 100,
|
||||
part1 to 30,
|
||||
part2 to 70
|
||||
),
|
||||
splittableParagraphs = mapOf(splittableBlock to (part1 to part2))
|
||||
)
|
||||
|
||||
val pages = paginate(blocks, pageHeight, measurementProvider, testDensity)
|
||||
assertThat(pages).hasSize(2)
|
||||
assertThat(pages[0].content).containsExactly(block1)
|
||||
assertThat(pages[1].content).containsExactly(splittableBlock) // Was not split
|
||||
}
|
||||
|
||||
@Test
|
||||
fun paginate_doesNotAddEmptyPart1AfterSplitting() = runTest {
|
||||
val originalBlock = ParagraphBlock(content = AnnotatedString("Some text"), blockIndex = 0)
|
||||
val part1 = ParagraphBlock(content = AnnotatedString(""), blockIndex = 0) // Empty part 1
|
||||
val part2 = ParagraphBlock(content = AnnotatedString("Some text"), blockIndex = 0)
|
||||
val blocks = listOf(originalBlock)
|
||||
|
||||
val measurementProvider = FakeSplittableMeasurementProvider(
|
||||
heights = mapOf(
|
||||
originalBlock to 200,
|
||||
part1 to 0,
|
||||
part2 to 200
|
||||
),
|
||||
splittableParagraphs = mapOf(originalBlock to (part1 to part2))
|
||||
)
|
||||
|
||||
// Set page height so that a split is attempted.
|
||||
val pages = paginate(blocks, 150, measurementProvider, testDensity)
|
||||
assertThat(pages).hasSize(1)
|
||||
// The page should be empty because part1 was empty, and the original block was re-added
|
||||
// to the remaining list. The next page then contains the full block.
|
||||
assertThat(pages[0].content).containsExactly(part2)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
// StyleUtilsTest.kt
|
||||
package com.aryan.reader.paginatedreader
|
||||
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.isUnspecified
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class StyleUtilsTest {
|
||||
|
||||
private val baseFontSizeSp = 16f
|
||||
private val density = 2.0f
|
||||
private val containerWidthPx = 1000
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesPxValues() {
|
||||
assertThat(parseCssSizeToDp("100px", baseFontSizeSp, density, containerWidthPx)).isEqualTo(50.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesEmValues() {
|
||||
assertThat(parseCssSizeToDp("1.5em", baseFontSizeSp, density, containerWidthPx)).isEqualTo(24.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesRemValues() {
|
||||
assertThat(parseCssSizeToDp("2rem", baseFontSizeSp, density, containerWidthPx)).isEqualTo(32.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesPtValues() {
|
||||
assertThat(parseCssSizeToDp("12pt", baseFontSizeSp, density, containerWidthPx).value).isWithin(0.01f).of(8.0f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesPercentageValues() {
|
||||
// 50% of 1000px = 500px. 500px / 2.0 density = 250dp
|
||||
assertThat(parseCssSizeToDp("50%", baseFontSizeSp, density, containerWidthPx)).isEqualTo(250.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_returns0ForInvalidInput() {
|
||||
assertThat(parseCssSizeToDp("invalid", baseFontSizeSp, density, containerWidthPx)).isEqualTo(0.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesZeroDensity() {
|
||||
assertThat(parseCssSizeToDp("100px", baseFontSizeSp, 0f, containerWidthPx)).isEqualTo(0.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesZeroContainerWidthForPercentage() {
|
||||
assertThat(parseCssSizeToDp("50%", baseFontSizeSp, density, 0)).isEqualTo(0.dp)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssSizeToDp_handlesValuesWithWhitespace() {
|
||||
assertThat(parseCssSizeToDp(" 1.5em ", baseFontSizeSp, density, containerWidthPx)).isEqualTo(24.dp)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesPxValues() {
|
||||
val result = parseCssDimensionToTextUnit("100px", containerWidthPx, density)
|
||||
assertThat(result.isSp).isTrue()
|
||||
assertThat(result.value).isWithin(0.01f).of(50f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesEmValues() {
|
||||
val result = parseCssDimensionToTextUnit("1.5em", containerWidthPx, density)
|
||||
assertThat(result.isEm).isTrue()
|
||||
assertThat(result.value).isEqualTo(1.5f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesRemValues() {
|
||||
// rem is treated as em
|
||||
val result = parseCssDimensionToTextUnit("2rem", containerWidthPx, density)
|
||||
assertThat(result.isEm).isTrue()
|
||||
assertThat(result.value).isEqualTo(2f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesPtValues() {
|
||||
val result = parseCssDimensionToTextUnit("12pt", containerWidthPx, density)
|
||||
assertThat(result.isSp).isTrue()
|
||||
assertThat(result.value).isWithin(0.01f).of(8.0f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesPercentageValues() {
|
||||
val result = parseCssDimensionToTextUnit("50%", containerWidthPx, density)
|
||||
assertThat(result.isSp).isTrue()
|
||||
assertThat(result.value).isWithin(0.01f).of(250f)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_returnsUnspecifiedForInvalidInput() {
|
||||
assertThat(parseCssDimensionToTextUnit("invalid", containerWidthPx, density).isUnspecified).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesZeroDensity() {
|
||||
assertThat(parseCssDimensionToTextUnit("100px", containerWidthPx, 0f).isUnspecified).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun parseCssDimensionToTextUnit_handlesZeroContainerWidthForPercentage() {
|
||||
assertThat(parseCssDimensionToTextUnit("50%", 0, density).isUnspecified).isTrue()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
// MainDispatcherRule.kt
|
||||
package com.aryan.reader.pdf
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
/**
|
||||
* A JUnit TestRule that sets the Main dispatcher to a TestDispatcher for the duration of a test.
|
||||
* This allows tests to execute coroutines on the Main dispatcher without needing a real Android environment.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MainDispatcherRule(
|
||||
val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
) : TestRule {
|
||||
override fun apply(base: Statement, description: Description): Statement {
|
||||
return object : Statement() {
|
||||
@Throws(Throwable::class)
|
||||
override fun evaluate() {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
try {
|
||||
base.evaluate()
|
||||
} finally {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,297 @@
|
|||
// PdfAnnotationTest.kt
|
||||
package com.aryan.reader.pdf
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertIsEnabled
|
||||
import androidx.compose.ui.test.assertIsNotEnabled
|
||||
import androidx.compose.ui.test.assertIsSelected
|
||||
import androidx.compose.ui.test.assertIsNotSelected
|
||||
import androidx.compose.ui.test.click
|
||||
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.aryan.reader.MainActivity
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PdfAnnotationTest {
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createEmptyComposeRule()
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
private var currentPdfFile: File? = null
|
||||
private var scenario: ActivityScenario<MainActivity>? = null
|
||||
private val samplePdfUri: Uri by lazy { copyAssetToCache(context, "sample.pdf") }
|
||||
|
||||
private fun createPdfViewIntent(context: Context, uri: Uri): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
data = uri
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
// Clear settings to ensure fresh state for every test
|
||||
context.getSharedPreferences("annotation_settings_global", Context.MODE_PRIVATE)
|
||||
.edit().clear().commit()
|
||||
context.getSharedPreferences("epub_reader_settings", Context.MODE_PRIVATE)
|
||||
.edit().clear().commit()
|
||||
|
||||
scenario = ActivityScenario.launch(createPdfViewIntent(context, samplePdfUri))
|
||||
waitForDocumentLoad()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
scenario?.close()
|
||||
currentPdfFile?.let { if (it.exists()) it.delete() }
|
||||
}
|
||||
|
||||
private fun waitForDocumentLoad() {
|
||||
composeTestRule.waitUntil(timeoutMillis = 15_000) {
|
||||
runCatching {
|
||||
composeTestRule.onNodeWithTag("PageNumberIndicator").assertIsDisplayed()
|
||||
true
|
||||
}.getOrDefault(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun enterEditMode() {
|
||||
composeTestRule.onNodeWithContentDescription("Toggle Drawing Mode")
|
||||
.assertIsDisplayed()
|
||||
.performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithContentDescription("Close Edit Mode").assertIsDisplayed()
|
||||
}
|
||||
|
||||
private fun tapOutsidePopup() {
|
||||
// Taps the center of the PDF viewer to dismiss popups
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").performTouchInput {
|
||||
click(center)
|
||||
}
|
||||
composeTestRule.waitForIdle()
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun copyAssetToCache(context: Context, assetName: String): Uri {
|
||||
val uniqueName = "${UUID.randomUUID()}_$assetName"
|
||||
val file = File(context.cacheDir, uniqueName)
|
||||
currentPdfFile = file
|
||||
if (file.exists()) file.delete()
|
||||
context.assets.open(assetName).use { inputStream ->
|
||||
file.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
return FileProvider.getUriForFile(context, "${context.packageName}.provider", file)
|
||||
}
|
||||
|
||||
// --- BASIC UI TESTS ---
|
||||
|
||||
@Test
|
||||
fun testEnterAndExitEditMode() {
|
||||
enterEditMode()
|
||||
|
||||
// Verify Dock Items exist using new Tags
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag("DockItem_Highlighter").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag("DockItem_Eraser").assertIsDisplayed()
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Close Edit Mode").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithContentDescription("Toggle Drawing Mode").assertIsDisplayed()
|
||||
}
|
||||
|
||||
// --- TOOL LOGIC TESTS ---
|
||||
|
||||
@Test
|
||||
fun testToolPersistence() {
|
||||
enterEditMode()
|
||||
|
||||
// 1. Select Highlighter
|
||||
composeTestRule.onNodeWithTag("DockItem_Highlighter").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 2. Verify selection state
|
||||
composeTestRule.onNodeWithTag("DockItem_Highlighter").assertIsSelected()
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").assertIsNotSelected()
|
||||
|
||||
// 3. Exit Edit Mode
|
||||
composeTestRule.onNodeWithContentDescription("Close Edit Mode").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 4. Re-enter Edit Mode
|
||||
enterEditMode()
|
||||
|
||||
// 5. Verify Highlighter is STILL selected (Persistence)
|
||||
composeTestRule.onNodeWithTag("DockItem_Highlighter").assertIsSelected()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEraserHasNoPopup() {
|
||||
enterEditMode()
|
||||
|
||||
// Select Eraser
|
||||
composeTestRule.onNodeWithTag("DockItem_Eraser").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithTag("DockItem_Eraser").assertIsSelected()
|
||||
|
||||
// Click Eraser AGAIN (Should NOT open popup)
|
||||
composeTestRule.onNodeWithTag("DockItem_Eraser").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
composeTestRule.onNodeWithTag("ToolSettingsPopup").assertDoesNotExist()
|
||||
}
|
||||
|
||||
// --- SETTINGS POPUP TESTS ---
|
||||
|
||||
@Test
|
||||
fun testSettingsPopupInteractions() {
|
||||
enterEditMode()
|
||||
|
||||
// 1. Pen is default. Click Pen ONCE to open Settings.
|
||||
// (Clicking twice would toggle it off, which caused previous failures)
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 2. Verify Popup Displayed
|
||||
composeTestRule.onNodeWithTag("ToolSettingsPopup").assertIsDisplayed()
|
||||
|
||||
// 3. Verify Pen Types exist
|
||||
composeTestRule.onNodeWithTag("SettingsItem_FOUNTAIN_PEN").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithTag("SettingsItem_MARKER").assertIsDisplayed()
|
||||
|
||||
// 4. Switch internal Pen Type
|
||||
composeTestRule.onNodeWithTag("SettingsItem_PENCIL").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithTag("SettingsItem_PENCIL").assertIsSelected()
|
||||
|
||||
// 5. Dismiss Settings
|
||||
tapOutsidePopup()
|
||||
composeTestRule.onNodeWithTag("ToolSettingsPopup").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testColorPaletteAndThickness() {
|
||||
enterEditMode()
|
||||
|
||||
// Open Settings for Pen (Default selected, so one click opens settings)
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// Test Palette Click (Index 1)
|
||||
composeTestRule.onNodeWithTag("Palette_Item_1").assertIsDisplayed().performClick()
|
||||
|
||||
// Test Thickness Buttons
|
||||
composeTestRule.onNodeWithTag("Property_Plus").performClick()
|
||||
composeTestRule.onNodeWithTag("Property_Plus").performClick()
|
||||
composeTestRule.onNodeWithTag("Property_Minus").performClick()
|
||||
|
||||
tapOutsidePopup()
|
||||
|
||||
// Quick verification that settings didn't crash app
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").assertIsDisplayed()
|
||||
}
|
||||
|
||||
// --- UNDO/REDO TESTS ---
|
||||
|
||||
@Test
|
||||
fun testDrawingEnablesUndo() {
|
||||
enterEditMode()
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Undo")
|
||||
.assertIsDisplayed()
|
||||
.assertIsNotEnabled()
|
||||
|
||||
// Draw a single DOT stroke to ensure exactly one action is recorded
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").performTouchInput {
|
||||
click(center)
|
||||
}
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Undo").assertIsEnabled()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testUndoRedoLogic() {
|
||||
enterEditMode()
|
||||
|
||||
// Draw 1 stroke (click = dot) to ensure stack size is exactly 1
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").performTouchInput {
|
||||
click(center)
|
||||
}
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
val undoNode = composeTestRule.onNodeWithContentDescription("Undo")
|
||||
val redoNode = composeTestRule.onNodeWithContentDescription("Redo")
|
||||
|
||||
undoNode.assertIsEnabled()
|
||||
redoNode.assertIsNotEnabled()
|
||||
|
||||
// Perform Undo
|
||||
undoNode.performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
undoNode.assertIsNotEnabled()
|
||||
redoNode.assertIsEnabled()
|
||||
|
||||
// Perform Redo
|
||||
redoNode.performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
undoNode.assertIsEnabled()
|
||||
redoNode.assertIsNotEnabled()
|
||||
}
|
||||
|
||||
// --- DOCK INTERACTIONS TESTS ---
|
||||
|
||||
@Test
|
||||
fun testDockMinimization() {
|
||||
enterEditMode()
|
||||
|
||||
// 1. Drag Dock to make it floating (using Pen icon as handle)
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").performTouchInput {
|
||||
down(center)
|
||||
advanceEventTime(600) // Long press
|
||||
// Drag UP significantly
|
||||
moveBy(androidx.compose.ui.geometry.Offset(0f, -600f), delayMillis = 1000)
|
||||
up()
|
||||
}
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 2. Minimize (Eye icon)
|
||||
composeTestRule.onNodeWithContentDescription("Toggle Visibility").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 3. Verify Dock items are hidden
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").assertDoesNotExist()
|
||||
|
||||
// 4. Verify "Show Dock" floating button is visible
|
||||
composeTestRule.onNodeWithContentDescription("Show Dock").assertIsDisplayed()
|
||||
|
||||
// 5. Restore
|
||||
composeTestRule.onNodeWithContentDescription("Show Dock").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// 6. Verify Dock items return
|
||||
composeTestRule.onNodeWithTag("DockItem_Pen").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,82 @@
|
|||
// app/src/androidTest/java/com/aryan/reader/pdf/PdfCoverGeneratorTest.kt
|
||||
package com.aryan.reader.pdf
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PdfCoverGeneratorTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var coverGenerator: PdfCoverGenerator
|
||||
private var samplePdfUri: Uri? = null
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = ApplicationProvider.getApplicationContext()
|
||||
coverGenerator = PdfCoverGenerator(context)
|
||||
try {
|
||||
samplePdfUri = copyAssetToCache(context, "sample.pdf")
|
||||
} catch (_: IOException) {
|
||||
println("Could not copy sample.pdf from assets. Skipping PdfCoverGenerator tests.")
|
||||
samplePdfUri = null
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
val cacheFile = File(context.cacheDir, "sample.pdf")
|
||||
if (cacheFile.exists()) {
|
||||
cacheFile.delete()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generateCover_returnsBitmapForValidPdf() = runTest {
|
||||
val uri = samplePdfUri ?: return@runTest
|
||||
|
||||
val targetHeight = 600
|
||||
val cover = coverGenerator.generateCover(uri, targetHeight)
|
||||
|
||||
assertThat(cover).isNotNull()
|
||||
assertThat(cover!!.height).isEqualTo(targetHeight)
|
||||
assertThat(cover.width).isGreaterThan(0)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun generateCover_returnsNullForInvalidUri() = runTest {
|
||||
val invalidUri = Uri.fromFile(File("nonexistent/file.pdf"))
|
||||
val cover = coverGenerator.generateCover(invalidUri)
|
||||
assertThat(cover).isNull()
|
||||
}
|
||||
|
||||
private fun copyAssetToCache(context: Context, @Suppress("SameParameterValue") assetName: String): Uri {
|
||||
val file = File(context.cacheDir, assetName)
|
||||
if (file.exists()) file.delete()
|
||||
context.assets.open(assetName).use { inputStream ->
|
||||
file.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
return FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.provider",
|
||||
file
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// app/src/androidTest/java/com/aryan/reader/pdf/PdfHelperTest.kt
|
||||
package com.aryan.reader.pdf
|
||||
|
||||
import android.graphics.Rect
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PdfHelperTest {
|
||||
|
||||
@Test
|
||||
fun mergeRectsIntoLines_mergesHorizontallyAdjacentRects() {
|
||||
val rects = listOf(
|
||||
Rect(0, 0, 10, 10),
|
||||
Rect(11, 0, 20, 10)
|
||||
)
|
||||
val merged = mergeRectsIntoLines(rects)
|
||||
assertThat(merged).hasSize(1)
|
||||
assertThat(merged.first()).isEqualTo(Rect(0, 0, 20, 10))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergeRectsIntoLines_doesNotMergeVerticallySeparatedRects() {
|
||||
val rects = listOf(
|
||||
Rect(0, 0, 10, 10),
|
||||
Rect(0, 11, 10, 20)
|
||||
)
|
||||
val merged = mergeRectsIntoLines(rects)
|
||||
assertThat(merged).hasSize(2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergeRectsIntoLines_handlesMultipleLines() {
|
||||
val rects = listOf(
|
||||
Rect(0, 0, 10, 10), // line 1
|
||||
Rect(11, 0, 20, 10), // line 1
|
||||
Rect(0, 15, 10, 25), // line 2
|
||||
Rect(11, 15, 20, 25) // line 2
|
||||
)
|
||||
val merged = mergeRectsIntoLines(rects)
|
||||
assertThat(merged).hasSize(2)
|
||||
assertThat(merged).containsExactly(
|
||||
Rect(0, 0, 20, 10),
|
||||
Rect(0, 15, 20, 25)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mergeRectsIntoLines_handlesEmptyList() {
|
||||
val merged = mergeRectsIntoLines(emptyList())
|
||||
assertThat(merged).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun preprocessTextForTts_replacesNewlineWithSpaceForSoftBreak() {
|
||||
val raw = "Hello\nWorld"
|
||||
val processed = preprocessTextForTts(raw)
|
||||
assertThat(processed.cleanText).isEqualTo("Hello World")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun preprocessTextForTts_handlesNewlineAfterPunctuation() {
|
||||
// Based on the current implementation, a newline after a sentence-ending punctuation
|
||||
// results in the words being concatenated without a space. This test verifies that behavior.
|
||||
val raw = "Hello.\nWorld"
|
||||
val processed = preprocessTextForTts(raw)
|
||||
assertThat(processed.cleanText).isEqualTo("Hello.World")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun preprocessTextForTts_handlesCarriageReturn() {
|
||||
val raw = "Hello\r\nWorld"
|
||||
val processed = preprocessTextForTts(raw)
|
||||
assertThat(processed.cleanText).isEqualTo("Hello World")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun preprocessTextForTts_trimsResult() {
|
||||
val raw = " Hello World \n"
|
||||
val processed = preprocessTextForTts(raw)
|
||||
assertThat(processed.cleanText).isEqualTo("Hello World")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,313 @@
|
|||
package com.aryan.reader.pdf
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.ui.test.assert
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.assertTextContains
|
||||
import androidx.compose.ui.test.hasTestTag
|
||||
import androidx.compose.ui.test.hasText
|
||||
import androidx.compose.ui.test.junit4.createEmptyComposeRule
|
||||
import androidx.compose.ui.test.onAllNodesWithText
|
||||
import androidx.compose.ui.test.onNodeWithContentDescription
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.compose.ui.test.performTextInput
|
||||
import androidx.compose.ui.test.performTouchInput
|
||||
import androidx.compose.ui.test.swipe
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.rules.ActivityScenarioRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import com.aryan.reader.MainActivity
|
||||
import org.junit.After
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.io.File
|
||||
import java.util.UUID
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class PdfViewerScreenTest {
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule = createEmptyComposeRule()
|
||||
|
||||
@get:Rule
|
||||
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(Manifest.permission.POST_NOTIFICATIONS)
|
||||
|
||||
@org.junit.Before
|
||||
fun setup() {
|
||||
val context = ApplicationProvider.getApplicationContext<Context>()
|
||||
context.getSharedPreferences("epub_reader_settings", Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
.clear()
|
||||
.commit()
|
||||
}
|
||||
|
||||
private fun createPdfViewIntent(context: Context, uri: Uri): Intent {
|
||||
return Intent(context, MainActivity::class.java).apply {
|
||||
action = Intent.ACTION_VIEW
|
||||
data = uri
|
||||
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
}
|
||||
}
|
||||
|
||||
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private var currentPdfFile: File? = null
|
||||
|
||||
private val samplePdfUri: Uri by lazy { copyAssetToCache(context, "sample.pdf") }
|
||||
|
||||
@get:Rule
|
||||
val activityRule = ActivityScenarioRule<MainActivity>(createPdfViewIntent(context, samplePdfUri))
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
currentPdfFile?.let {
|
||||
if (it.exists()) it.delete()
|
||||
}
|
||||
}
|
||||
|
||||
private fun waitForDocumentLoad(pageText: String = "Page 1 of 4") {
|
||||
composeTestRule.waitUntil(timeoutMillis = 15_000) {
|
||||
composeTestRule
|
||||
.onAllNodesWithText(pageText)
|
||||
.fetchSemanticsNodes().size == 1
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensurePaginationMode() {
|
||||
composeTestRule.onNodeWithContentDescription("More Options").performClick()
|
||||
composeTestRule.onNodeWithText("Reading Mode: Paginated").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun documentLoadsAndDisplaysCorrectPageCount() {
|
||||
waitForDocumentLoad()
|
||||
composeTestRule.onNodeWithTag("PageNumberIndicator")
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tableOfContents_displaysEmptyState() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
composeTestRule.onNodeWithTag("TocButton").performClick()
|
||||
|
||||
composeTestRule.onNodeWithText("Chapters are not available for this book.").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Suppress("SameParameterValue")
|
||||
private fun copyAssetToCache(context: Context, assetName: String): Uri {
|
||||
val uniqueName = "${UUID.randomUUID()}_$assetName"
|
||||
val file = File(context.cacheDir, uniqueName)
|
||||
|
||||
currentPdfFile = file
|
||||
|
||||
if (file.exists()) file.delete()
|
||||
context.assets.open(assetName).use { inputStream ->
|
||||
file.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
return FileProvider.getUriForFile(
|
||||
context,
|
||||
"${context.packageName}.provider",
|
||||
file
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bookmarkFunctionality_addNavigateAndDelete() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
ensurePaginationMode()
|
||||
|
||||
composeTestRule.onNodeWithText("Page 1 of 4").assertIsDisplayed()
|
||||
|
||||
try {
|
||||
composeTestRule.onRoot().performTouchInput { swipe(start = this.centerRight, end = this.centerLeft, durationMillis = 300) }
|
||||
composeTestRule.onRoot().performClick()
|
||||
composeTestRule.waitUntil(5_000) {
|
||||
composeTestRule.onAllNodesWithText("Page 2 of 4").fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
composeTestRule.onNodeWithText("Page 2 of 4").assertIsDisplayed()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithContentDescription("More Options").performClick()
|
||||
composeTestRule.onNodeWithText("Bookmark this page").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onRoot().performTouchInput { swipe(start = this.centerRight, end = this.centerLeft, durationMillis = 300) }
|
||||
composeTestRule.onRoot().performClick()
|
||||
composeTestRule.waitUntil(5_000) {
|
||||
composeTestRule.onAllNodesWithText("Page 3 of 4").fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
composeTestRule.onNodeWithText("Page 3 of 4").assertIsDisplayed()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithTag("TocButton").performClick()
|
||||
composeTestRule.onNodeWithTag("BookmarksTab").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.onNodeWithTag("BookmarkItem_1").assertIsDisplayed()
|
||||
.assert(hasText("Page 2", substring = true))
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithTag("BookmarkItem_1").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
composeTestRule.waitUntil(5_000) {
|
||||
composeTestRule.onAllNodes(hasTestTag("PageNumberIndicator").and(hasText("Page 2 of 4"))).fetchSemanticsNodes().size == 1
|
||||
}
|
||||
composeTestRule.onNode(hasTestTag("PageNumberIndicator").and(hasText("Page 2 of 4"))).assertIsDisplayed()
|
||||
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithTag("TocButton").performClick()
|
||||
composeTestRule.onNodeWithTag("BookmarksTab").performClick()
|
||||
composeTestRule.waitForIdle()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithContentDescription("More options for bookmark").performClick()
|
||||
composeTestRule.onNodeWithText("Delete").performClick()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithText("Delete", useUnmergedTree = true).performClick()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
|
||||
try {
|
||||
composeTestRule.onNodeWithTag("BookmarkItem_1").assertDoesNotExist()
|
||||
composeTestRule.onNodeWithText("You haven't added any bookmarks yet.").assertIsDisplayed()
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun sliderNavigation_opensAndDisplaysCorrectly() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
composeTestRule.onNodeWithContentDescription("Navigate with slider").performClick()
|
||||
composeTestRule.onNodeWithContentDescription("Exit slider navigation").assertIsDisplayed()
|
||||
composeTestRule.onNodeWithText("1 / 4").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayMode_switchesToVerticalScroll() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
// Ensure we are in Pagination mode first to test the switch
|
||||
ensurePaginationMode()
|
||||
|
||||
// Verify Vertical Scroll component is NOT displayed initially
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").assertDoesNotExist()
|
||||
|
||||
// Switch to Vertical Scroll
|
||||
composeTestRule.onNodeWithContentDescription("More Options").performClick()
|
||||
composeTestRule.onNodeWithText("Reading Mode: Vertical scroll").performClick()
|
||||
|
||||
composeTestRule.waitForIdle()
|
||||
|
||||
// Verify Vertical Scroll component IS displayed
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").assertIsDisplayed()
|
||||
|
||||
// Switch back to Pagination
|
||||
ensurePaginationMode()
|
||||
|
||||
// Verify Vertical Scroll component is gone
|
||||
composeTestRule.onNodeWithTag("PdfVerticalScroll").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun search_uiOpensAndAcceptsQuery() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
// Click search button
|
||||
composeTestRule.onNodeWithTag("SearchButton").performClick()
|
||||
|
||||
composeTestRule.onNodeWithText("English, Spanish, French, etc.").performClick()
|
||||
|
||||
// Verify text field appears
|
||||
composeTestRule.onNodeWithTag("SearchTextField").assertIsDisplayed()
|
||||
|
||||
// Enter text
|
||||
composeTestRule.onNodeWithTag("SearchTextField").performTextInput("test query")
|
||||
|
||||
// Verify text exists in the field
|
||||
composeTestRule.onNodeWithTag("SearchTextField").assertTextContains("test query")
|
||||
|
||||
// Close search
|
||||
composeTestRule.onNodeWithContentDescription("Close Search").performClick()
|
||||
|
||||
// Verify text field is gone
|
||||
composeTestRule.onNodeWithTag("SearchTextField").assertDoesNotExist()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun fullScreen_togglesVisibility() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
// Click enter full screen button
|
||||
composeTestRule.onNodeWithContentDescription("Enter Full Screen").performClick()
|
||||
|
||||
// Verify exit full screen button appears
|
||||
composeTestRule.onNodeWithContentDescription("Exit Full Screen").assertIsDisplayed()
|
||||
|
||||
// Click exit full screen
|
||||
composeTestRule.onNodeWithContentDescription("Exit Full Screen").performClick()
|
||||
|
||||
// Verify exit button is gone and enter button returns
|
||||
composeTestRule.onNodeWithContentDescription("Exit Full Screen").assertDoesNotExist()
|
||||
composeTestRule.onNodeWithContentDescription("Enter Full Screen").assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun darkMode_togglesState() {
|
||||
waitForDocumentLoad()
|
||||
|
||||
// Initial state: Light mode (default from cleared prefs), so button says "Enable Dark Mode"
|
||||
composeTestRule.onNodeWithContentDescription("Enable Dark Mode").assertIsDisplayed()
|
||||
|
||||
// Toggle On
|
||||
composeTestRule.onNodeWithContentDescription("Enable Dark Mode").performClick()
|
||||
|
||||
// State changed: Now button says "Disable Dark Mode"
|
||||
composeTestRule.onNodeWithContentDescription("Disable Dark Mode").assertIsDisplayed()
|
||||
|
||||
// Toggle Off
|
||||
composeTestRule.onNodeWithContentDescription("Disable Dark Mode").performClick()
|
||||
|
||||
// State changed back
|
||||
composeTestRule.onNodeWithContentDescription("Enable Dark Mode").assertIsDisplayed()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
// BaseTtsSynthesizerTest.kt
|
||||
package com.aryan.reader.tts
|
||||
|
||||
import android.speech.tts.TextToSpeech
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class BaseTtsSynthesizerTest {
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private lateinit var synthesizer: BaseTtsSynthesizer
|
||||
private var ttsEngineAvailable = false
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Ensure TTS is available on the device/emulator before running tests
|
||||
val tts = TextToSpeech(ApplicationProvider.getApplicationContext(), null)
|
||||
if (tts.engines.isNotEmpty()) {
|
||||
ttsEngineAvailable = true
|
||||
synthesizer = BaseTtsSynthesizer(ApplicationProvider.getApplicationContext())
|
||||
}
|
||||
tts.shutdown()
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
if (this::synthesizer.isInitialized) {
|
||||
synthesizer.shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun initialize_initializesTtsEngineSuccessfully() {
|
||||
if (!ttsEngineAvailable) return
|
||||
|
||||
runBlocking {
|
||||
// This will throw if it fails
|
||||
withTimeout(10000L) {
|
||||
synthesizer.initialize()
|
||||
}
|
||||
// No assertion needed, success is not throwing an exception.
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun synthesizeToFile_withValidText_createsAudioFile() {
|
||||
if (!ttsEngineAvailable) return
|
||||
|
||||
runBlocking {
|
||||
withTimeout(15000L) {
|
||||
synthesizer.initialize()
|
||||
val (file, returnedText) = synthesizer.synthesizeToFile("This is a test.")
|
||||
|
||||
assertThat(file).isNotNull()
|
||||
assertThat(file?.exists()).isTrue()
|
||||
assertThat(file?.length()).isGreaterThan(0L)
|
||||
assertThat(returnedText).isEqualTo("This is a test.")
|
||||
|
||||
file?.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun synthesizeToFile_withBlankText_returnsNullFile() {
|
||||
if (!ttsEngineAvailable) return
|
||||
|
||||
runBlocking {
|
||||
synthesizer.initialize()
|
||||
val (file, returnedText) = synthesizer.synthesizeToFile(" ")
|
||||
|
||||
assertThat(file).isNull()
|
||||
assertThat(returnedText).isEqualTo(" ")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun synthesizeToFile_withoutInitializingFirst_initializesAndSucceeds() {
|
||||
if (!ttsEngineAvailable) return
|
||||
|
||||
runBlocking {
|
||||
withTimeout(15000L) {
|
||||
val (file, returnedText) = synthesizer.synthesizeToFile("This should work.")
|
||||
|
||||
assertThat(file).isNotNull()
|
||||
assertThat(file?.exists()).isTrue()
|
||||
assertThat(file?.length()).isGreaterThan(0L)
|
||||
assertThat(returnedText).isEqualTo("This should work.")
|
||||
|
||||
file?.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package com.aryan.reader.tts
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.TestDispatcher
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.resetMain
|
||||
import kotlinx.coroutines.test.setMain
|
||||
import org.junit.rules.TestWatcher
|
||||
import org.junit.runner.Description
|
||||
|
||||
/**
|
||||
* A JUnit TestRule that sets the Main dispatcher to a TestDispatcher for the duration of a test.
|
||||
* This allows tests to execute coroutines on the Main dispatcher without needing a real Android environment.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class MainDispatcherRule(
|
||||
private val testDispatcher: TestDispatcher = UnconfinedTestDispatcher()
|
||||
) : TestWatcher() {
|
||||
override fun starting(description: Description) {
|
||||
Dispatchers.setMain(testDispatcher)
|
||||
}
|
||||
|
||||
override fun finished(description: Description) {
|
||||
Dispatchers.resetMain()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
// TtsUtilsTest.kt
|
||||
package com.aryan.reader.tts
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TtsUtilsTest {
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_withShortText_returnsSingleChunk() {
|
||||
val text = "This is a short sentence."
|
||||
val chunks = splitTextIntoChunks(text, 100)
|
||||
assertThat(chunks).containsExactly("This is a short sentence.")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_withMultipleSentences_splitsCorrectly() {
|
||||
val text = "First sentence. Second sentence! Third sentence? And a fourth."
|
||||
val chunks = splitTextIntoChunks(text, 20)
|
||||
assertThat(chunks).containsExactly(
|
||||
"First sentence.",
|
||||
"Second sentence!",
|
||||
"Third sentence?",
|
||||
"And a fourth."
|
||||
).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_combinesShortSentences() {
|
||||
val text = "First. Second. Third. Fourth."
|
||||
val chunks = splitTextIntoChunks(text, maxLengthPerChunk = 20)
|
||||
assertThat(chunks).containsExactly(
|
||||
"First. Second.",
|
||||
"Third. Fourth."
|
||||
).inOrder()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_withLongSentence_doesNotSplitSentence() {
|
||||
val text = "This is a very long sentence that exceeds the maximum chunk length but has no punctuation to split on."
|
||||
val chunks = splitTextIntoChunks(text, 50)
|
||||
assertThat(chunks).containsExactly(text)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_withEmptyText_returnsEmptyList() {
|
||||
val text = ""
|
||||
val chunks = splitTextIntoChunks(text, 100)
|
||||
assertThat(chunks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_withBlankText_returnsEmptyList() {
|
||||
val text = " "
|
||||
val chunks = splitTextIntoChunks(text, 100)
|
||||
assertThat(chunks).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun splitTextIntoChunks_handlesAbbreviations() {
|
||||
val text = "Mr. Smith went to Washington. Dr. Jones followed."
|
||||
val chunks = splitTextIntoChunks(text, 40)
|
||||
assertThat(chunks).containsExactly(
|
||||
"Mr. Smith went to Washington.",
|
||||
"Dr. Jones followed."
|
||||
).inOrder()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.aryan.reader.epubreader
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.aryan.reader.RenderMode
|
||||
import com.aryan.reader.epub.EpubBook
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class EpubTestActivity : ComponentActivity() {
|
||||
@RequiresApi(Build.VERSION_CODES.VANILLA_ICE_CREAM)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
val bookJson = intent.getStringExtra("epubBookJson")
|
||||
val book = Json.decodeFromString<EpubBook>(bookJson!!)
|
||||
|
||||
setContent {
|
||||
EpubReaderScreen(
|
||||
epubBook = book,
|
||||
renderMode = RenderMode.VERTICAL_SCROLL,
|
||||
initialLocator = null,
|
||||
initialCfi = null,
|
||||
initialBookmarksJson = null,
|
||||
isProUser = false,
|
||||
pendingSyncUpdate = null,
|
||||
onClearPendingSyncUpdate = {},
|
||||
onNavigateBack = {},
|
||||
onSavePosition = { _, _, _ -> },
|
||||
onBookmarksChanged = {},
|
||||
onNavigateToPro = {},
|
||||
coverImagePath = null,
|
||||
onRenderModeChange = {},
|
||||
customFonts = TODO(),
|
||||
onImportFont = TODO()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.aryan.reader.epubreader
|
||||
|
||||
import androidx.activity.ComponentActivity
|
||||
|
||||
/**
|
||||
* A simple, empty activity used as a host for Compose UI tests.
|
||||
* It allows tests to launch a Compose view without needing the app's full
|
||||
* navigation or main activity setup. It should be placed in the `debug`
|
||||
* source set (`app/src/debug/java/...`) to ensure it is not included
|
||||
* in the release build of your app.
|
||||
*/
|
||||
class HiltTestActivity : ComponentActivity()
|
||||
126
app/src/main/AndroidManifest.xml
Normal file
126
app/src/main/AndroidManifest.xml
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.TTS_SERVICE" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:name=".MyApplication"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Reader"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.Reader"
|
||||
android:launchMode="singleTask">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- PDF Filter -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="application/pdf" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- EPUB Filter -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="application/epub+zip" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- MOBI / Kindle Filter -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="application/x-mobipocket-ebook" />
|
||||
<data android:mimeType="application/vnd.amazon.mobi8-ebook" />
|
||||
<data android:mimeType="application/vnd.amazon.ebook" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
<data android:host="*" />
|
||||
<data android:pathPattern=".*\\.mobi" />
|
||||
<data android:pathPattern=".*\\.azw3" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Markdown and Plain Text Filter -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="text/markdown" />
|
||||
<data android:mimeType="text/x-markdown" />
|
||||
<data android:mimeType="text/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:scheme="file" />
|
||||
<data android:host="*" />
|
||||
<data android:mimeType="*/*" />
|
||||
<data android:pathPattern=".*\\.md" />
|
||||
<data android:pathPattern=".*\\.markdown" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="content" />
|
||||
<data android:mimeType="application/octet-stream" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".tts.TtsService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaPlayback">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<provider
|
||||
android:name="androidx.core.content.FileProvider"
|
||||
android:authorities="${applicationId}.provider"
|
||||
android:exported="false"
|
||||
android:grantUriPermissions="true">
|
||||
<meta-data
|
||||
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||
android:resource="@xml/provider_paths" />
|
||||
</provider>
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
35
app/src/main/assets/MathML-template.html
Normal file
35
app/src/main/assets/MathML-template.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>
|
||||
console.log("MATH_DIAGNOSTIC: MathML-template.html script block started.");
|
||||
window.MathJax = {
|
||||
svg: {
|
||||
fontCache: 'none', // Disable font cache for better performance in this context
|
||||
displayAlign: 'left',
|
||||
displayIndent: '0',
|
||||
scale: 1.0,
|
||||
},
|
||||
startup: {
|
||||
ready: () => {
|
||||
console.log("MATH_DIAGNOSTIC: MathJax startup.ready callback fired.");
|
||||
MathJax.startup.defaultReady();
|
||||
if (window.AndroidBridge) {
|
||||
console.log("MATH_DIAGNOSTIC: Calling AndroidBridge.onMathJaxReady()");
|
||||
window.AndroidBridge.onMathJaxReady();
|
||||
} else {
|
||||
console.error("MATH_DIAGNOSTIC: AndroidBridge not found!");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" id="MathJax-script" async
|
||||
src="file:///android_asset/mathjax/tex-mml-svg.js">
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="math-container"></div>
|
||||
</body>
|
||||
</html>
|
||||
70
app/src/main/assets/demo_art.svg
Normal file
70
app/src/main/assets/demo_art.svg
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
<svg viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="underlineGradient" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:#F59E0B;stop-opacity:1" />
|
||||
<stop offset="50%" style="stop-color:#EC4899;stop-opacity:1" />
|
||||
<stop offset="100%" style="stop-color:#8B5CF6;stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
|
||||
<!-- Background -->
|
||||
<rect width="800" height="300" fill="#f0fdf4"/>
|
||||
|
||||
<!-- Decorative dots -->
|
||||
<circle cx="120" cy="90" r="5" fill="#F59E0B" opacity="0.7" />
|
||||
<circle cx="680" cy="210" r="6" fill="#EC4899" opacity="0.7" />
|
||||
<circle cx="700" cy="110" r="4" fill="#8B5CF6" opacity="0.7" />
|
||||
<circle cx="150" cy="230" r="5" fill="#10B981" opacity="0.6" />
|
||||
<circle cx="650" cy="80" r="4" fill="#F59E0B" opacity="0.6" />
|
||||
<circle cx="90" cy="180" r="3" fill="#EC4899" opacity="0.5" />
|
||||
<circle cx="720" cy="170" r="5" fill="#8B5CF6" opacity="0.6" />
|
||||
|
||||
<!-- Single-stroke handwritten "Try Episteme!" -->
|
||||
<g stroke="#10B981" stroke-width="3.5" fill="none" stroke-linecap="round" stroke-linejoin="round">
|
||||
<!-- T -->
|
||||
<path d="M 80 115 L 140 115 M 110 115 L 110 175 Q 110 185 115 185" />
|
||||
|
||||
<!-- r -->
|
||||
<path d="M 150 145 L 150 180 M 150 155 Q 155 145 165 145 Q 172 145 175 150" />
|
||||
|
||||
<!-- y -->
|
||||
<path d="M 185 145 L 190 180 L 200 145 L 198 185 Q 197 200 188 210 Q 182 215 176 210" />
|
||||
|
||||
<!-- Space, then E -->
|
||||
<path d="M 240 110 L 240 180 M 240 110 L 285 110 M 240 145 L 275 145 M 240 180 L 285 180" />
|
||||
|
||||
<!-- p -->
|
||||
<path d="M 305 145 L 305 215 M 305 158 Q 305 145 320 145 Q 340 145 345 160 Q 348 170 345 180 Q 340 195 320 195 Q 305 195 305 182" />
|
||||
|
||||
<!-- i -->
|
||||
<path d="M 365 145 L 365 180 M 365 130 L 365 132" />
|
||||
|
||||
<!-- s -->
|
||||
<path d="M 428 148 Q 418 143 408 145 Q 398 147 395 155 Q 393 162 400 165 Q 410 170 420 168 Q 428 166 430 172 Q 432 180 422 183 Q 412 186 402 182" />
|
||||
|
||||
<!-- t -->
|
||||
<path d="M 445 125 L 445 175 Q 445 185 455 185 Q 465 185 470 180 M 435 145 L 460 145" />
|
||||
|
||||
<!-- e -->
|
||||
<path d="M 530 158 L 490 160 Q 488 165 490 172 Q 493 182 505 185 Q 520 187 528 178 Q 533 170 528 160 Q 520 148 505 148 Q 495 148 490 153" />
|
||||
|
||||
<!-- m -->
|
||||
<path d="M 545 145 L 545 180 M 545 155 Q 545 145 555 145 Q 565 145 565 155 L 565 180 M 565 155 Q 565 145 575 145 Q 585 145 585 155 L 585 180" />
|
||||
|
||||
<!-- e (second) -->
|
||||
<path d="M 635 158 L 600 160 Q 598 165 600 172 Q 603 182 615 185 Q 628 187 635 178 Q 640 170 635 160 Q 628 148 613 148 Q 603 148 598 153" />
|
||||
|
||||
<!-- ! -->
|
||||
<path d="M 660 125 L 660 165 M 660 178 L 660 182" />
|
||||
</g>
|
||||
|
||||
<!-- Decorative underline -->
|
||||
<path
|
||||
d="M 180 200 Q 400 220 620 200"
|
||||
stroke="#EC4899"
|
||||
stroke-width="3"
|
||||
fill="none"
|
||||
opacity="0.6"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
1775
app/src/main/assets/epub_reader.js
Normal file
1775
app/src/main/assets/epub_reader.js
Normal file
File diff suppressed because it is too large
Load diff
BIN
app/src/main/assets/fonts/lato.ttf
Normal file
BIN
app/src/main/assets/fonts/lato.ttf
Normal file
Binary file not shown.
BIN
app/src/main/assets/fonts/lexend.ttf
Normal file
BIN
app/src/main/assets/fonts/lexend.ttf
Normal file
Binary file not shown.
BIN
app/src/main/assets/fonts/lora.ttf
Normal file
BIN
app/src/main/assets/fonts/lora.ttf
Normal file
Binary file not shown.
BIN
app/src/main/assets/fonts/merriweather.ttf
Normal file
BIN
app/src/main/assets/fonts/merriweather.ttf
Normal file
Binary file not shown.
BIN
app/src/main/assets/fonts/roboto_mono.ttf
Normal file
BIN
app/src/main/assets/fonts/roboto_mono.ttf
Normal file
Binary file not shown.
1
app/src/main/assets/mathjax/tex-mml-chtml.js
Normal file
1
app/src/main/assets/mathjax/tex-mml-chtml.js
Normal file
File diff suppressed because one or more lines are too long
1
app/src/main/assets/mathjax/tex-mml-svg.js
Normal file
1
app/src/main/assets/mathjax/tex-mml-svg.js
Normal file
File diff suppressed because one or more lines are too long
BIN
app/src/main/assets/sample.pdf
Normal file
BIN
app/src/main/assets/sample.pdf
Normal file
Binary file not shown.
13
app/src/main/assets/test_chapter_1.html
Normal file
13
app/src/main/assets/test_chapter_1.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!-- assets\test_chapter_1.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Test Chapter 1</title>
|
||||
</head>
|
||||
<body>
|
||||
<p id='first_para'>Hello World</p>
|
||||
<p>This is a test chapter for UI testing.</p>
|
||||
<p id='search_target'>You can search for the word Espresso.</p>
|
||||
<p>Another paragraph with the word espresso but lowercase.</p>
|
||||
</body>
|
||||
</html>
|
||||
10
app/src/main/assets/test_chapter_2.html
Normal file
10
app/src/main/assets/test_chapter_2.html
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
<!-- assets\test_chapter_2.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Test Chapter 2</title>
|
||||
</head>
|
||||
<body>
|
||||
<p id='second_chapter_para'>This is the second chapter.</p>
|
||||
</body>
|
||||
</html>
|
||||
95
app/src/main/cpp/CMakeLists.txt
vendored
Normal file
95
app/src/main/cpp/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
# CMakeLists.txt
|
||||
# Sets the minimum version of CMake required.
|
||||
cmake_minimum_required(VERSION 3.22.1)
|
||||
|
||||
# Declares the project name.
|
||||
project("reader-native")
|
||||
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
|
||||
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,max-page-size=16384")
|
||||
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC")
|
||||
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# --- CONFIGURE SUBPROJECTS ---
|
||||
# Force subprojects to build as static libraries. This is critical for Android.
|
||||
set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build shared libraries" FORCE)
|
||||
set(BROTLI_DISABLE_TESTS ON CACHE BOOL "Disable Brotli tests" FORCE)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# WOFF2 DEPENDENCY SETUP
|
||||
# ===================================================================
|
||||
# 1. Add the brotli project. This defines the `brotlidec-static` target.
|
||||
add_subdirectory(woff2/brotli)
|
||||
|
||||
# 2. Manually define the variables that the woff2/CMakeLists.txt script expects.
|
||||
set(BROTLIDEC_FOUND TRUE)
|
||||
set(BROTLIENC_FOUND TRUE)
|
||||
set(BROTLIDEC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/woff2/brotli/c/include)
|
||||
set(BROTLIENC_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/woff2/brotli/c/include)
|
||||
set(BROTLIDEC_LIBRARIES brotlidec-static)
|
||||
set(BROTLIENC_LIBRARIES brotlienc-static)
|
||||
|
||||
# 3. Add the woff2 project. This defines the `woff2dec` target.
|
||||
add_subdirectory(woff2)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# LIBMOBI DEPENDENCY SETUP
|
||||
# ===================================================================
|
||||
# 4. Set libmobi options before adding it.
|
||||
# We disable libxml2 to use the internal writer, simplifying dependencies.
|
||||
# We also disable encryption for now for the same reason.
|
||||
set(USE_LIBXML2 OFF CACHE BOOL "Use libxml2" FORCE)
|
||||
set(USE_ENCRYPTION OFF CACHE BOOL "Enable encryption" FORCE)
|
||||
|
||||
# Temporarily enable shared libs to build libmobi as a .so file for LGPL compliance.
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
# 5. Add the libmobi project. This will define the `mobi` target as a shared library.
|
||||
add_subdirectory(libmobi)
|
||||
|
||||
set_target_properties(mobi PROPERTIES VERSION "" SOVERSION "")
|
||||
# Revert back to building static libs for any subsequent dependencies.
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
# Force all symbols in the mobi shared library to be visible.
|
||||
# This is necessary because some internal functions we use (like mobi_determine_flowpart_type)
|
||||
# are not explicitly exported by the library's public API for shared builds.
|
||||
set_target_properties(mobi PROPERTIES C_VISIBILITY_PRESET default)
|
||||
|
||||
|
||||
# ===================================================================
|
||||
# FINAL NATIVE LIBRARY FOR THE APP
|
||||
# ===================================================================
|
||||
# 6. Define our final JNI wrapper library.
|
||||
# This single .so file will be loaded by the Android app.
|
||||
add_library(
|
||||
native-lib
|
||||
SHARED
|
||||
Woff2Converter.cpp
|
||||
mobi_jni_bridge.c # The placeholder file you created
|
||||
)
|
||||
|
||||
# 7. Tell our library where to find all necessary header files.
|
||||
target_include_directories(native-lib
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/woff2/include
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/libmobi/src
|
||||
)
|
||||
|
||||
# 8. Find the Android logging and zlib libraries.
|
||||
find_library(log-lib log)
|
||||
find_library(z-lib z)
|
||||
|
||||
# 9. Link our final library against all the static libraries and Android libraries.
|
||||
target_link_libraries(
|
||||
native-lib
|
||||
PRIVATE
|
||||
woff2dec # From woff2
|
||||
mobi # From libmobi
|
||||
${log-lib}
|
||||
${z-lib} # libmobi requires zlib
|
||||
)
|
||||
56
app/src/main/cpp/Woff2Converter.cpp
vendored
Normal file
56
app/src/main/cpp/Woff2Converter.cpp
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
// This is the correct header for the public API
|
||||
#include <woff2/decode.h>
|
||||
|
||||
extern "C" JNIEXPORT jbyteArray JNICALL
|
||||
Java_com_aryan_reader_paginatedreader_Woff2Converter_convertWoff2ToTtf(
|
||||
JNIEnv *env,
|
||||
jobject /* this */,
|
||||
jbyteArray woff2_data) {
|
||||
|
||||
// Get the input WOFF2 data from the jbyteArray
|
||||
jbyte* woff2_bytes = env->GetByteArrayElements(woff2_data, nullptr);
|
||||
jsize woff2_size = env->GetArrayLength(woff2_data);
|
||||
const uint8_t* woff2_input = reinterpret_cast<const uint8_t*>(woff2_bytes);
|
||||
|
||||
// Calculate the required size using the correct function name from your header
|
||||
size_t ttf_size = woff2::ComputeWOFF2FinalSize(woff2_input, woff2_size);
|
||||
if (ttf_size == 0) {
|
||||
// This indicates an error in the input font data
|
||||
env->ReleaseByteArrayElements(woff2_data, woff2_bytes, JNI_ABORT);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create the output buffer
|
||||
std::vector<uint8_t> ttf_output(ttf_size);
|
||||
|
||||
// Perform the conversion using the deprecated function signature that matches your header
|
||||
bool success = woff2::ConvertWOFF2ToTTF(
|
||||
ttf_output.data(), ttf_size,
|
||||
woff2_input, woff2_size
|
||||
);
|
||||
|
||||
// Release the input byte array
|
||||
env->ReleaseByteArrayElements(woff2_data, woff2_bytes, JNI_ABORT);
|
||||
|
||||
// If conversion failed, return null
|
||||
if (!success) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Create a new Java byte array for the result
|
||||
jbyteArray ttf_data = env->NewByteArray(ttf_size);
|
||||
if (ttf_data == nullptr) {
|
||||
// Out of memory error
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Copy the converted data to the Java byte array
|
||||
env->SetByteArrayRegion(ttf_data, 0, ttf_size,
|
||||
reinterpret_cast<const jbyte*>(ttf_output.data()));
|
||||
|
||||
return ttf_data;
|
||||
}
|
||||
101
app/src/main/cpp/libmobi/.github/workflows/build.yml
vendored
Normal file
101
app/src/main/cpp/libmobi/.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
name: Build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ public ]
|
||||
pull_request:
|
||||
branches: [ public ]
|
||||
|
||||
jobs:
|
||||
unix-build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- name: default build with debug
|
||||
options: --enable-debug
|
||||
- name: bulid with internal libs
|
||||
options: --with-zlib=no --with-libxml2=no
|
||||
- name: build without encryption
|
||||
options: --disable-encryption
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: install dependencies
|
||||
run: |
|
||||
if [ "${{ runner.os }}" = "Linux" ]; then
|
||||
sudo apt-get update -qq;
|
||||
sudo apt-get install -y autotools-dev pkg-config automake autoconf libtool;
|
||||
sudo apt-get install -y zlib1g-dev libxml2-dev;
|
||||
elif [ "${{ runner.os }}" = "macOS" ]; then
|
||||
brew update > /dev/null;
|
||||
brew outdated autoconf || brew upgrade autoconf;
|
||||
brew outdated automake || brew upgrade automake;
|
||||
brew outdated libtool || brew upgrade libtool;
|
||||
fi
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{ matrix.config.options }}
|
||||
- name: make
|
||||
run: make -j `nproc`
|
||||
- name: make check
|
||||
run: make -j `nproc` check
|
||||
- name: make distcheck
|
||||
run: make -j `nproc` distcheck
|
||||
- name: upload debug artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: test-logs-${{ matrix.runs-on }}
|
||||
path: |
|
||||
**/tests/test-suite.log
|
||||
**/tests/samples/*.log
|
||||
|
||||
win64-build:
|
||||
|
||||
runs-on: windows-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
steps:
|
||||
- name: setup-msys2
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: MINGW64
|
||||
path-type: minimal
|
||||
update: true
|
||||
install: >-
|
||||
git
|
||||
autotools
|
||||
base-devel
|
||||
mingw-w64-x86_64-toolchain
|
||||
mingw-w64-x86_64-libtool
|
||||
mingw-w64-x86_64-libxml2
|
||||
mingw-w64-x86_64-zlib
|
||||
- name: checkout
|
||||
uses: actions/checkout@v4
|
||||
- name: autogen
|
||||
run: sh ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure --enable-debug
|
||||
- name: make
|
||||
run: make -j$(nproc)
|
||||
- name: make check
|
||||
run: make -j$(nproc) check
|
||||
- name: make distcheck
|
||||
run: make -j$(nproc) distcheck
|
||||
- name: upload debug artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
if: ${{ failure() }}
|
||||
with:
|
||||
name: test-logs-${{ matrix.runs-on }}
|
||||
path: |
|
||||
**/tests/test-suite.log
|
||||
**/tests/samples/*.log
|
||||
71
app/src/main/cpp/libmobi/.github/workflows/codeql-analysis.yml
vendored
Normal file
71
app/src/main/cpp/libmobi/.github/workflows/codeql-analysis.yml
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# For most projects, this workflow file will not need changing; you simply need
|
||||
# to commit it to your repository.
|
||||
#
|
||||
# You may wish to alter this file to override the set of languages analyzed,
|
||||
# or to provide custom queries or build logic.
|
||||
#
|
||||
# ******** NOTE ********
|
||||
# We have attempted to detect the languages in your repository. Please check
|
||||
# the `language` matrix defined below to confirm you have the correct set of
|
||||
# supported CodeQL languages.
|
||||
#
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ public ]
|
||||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ public ]
|
||||
schedule:
|
||||
- cron: '19 13 * * 3'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
# Learn more:
|
||||
# https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
# By default, queries listed here will override any specified in a config file.
|
||||
# Prefix the list here with "+" to use these queries and those in the config file.
|
||||
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
||||
# and modify them (or add more) to build your code if your project
|
||||
# uses a compiled language
|
||||
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
54
app/src/main/cpp/libmobi/.github/workflows/coverity-scan.yml
vendored
Normal file
54
app/src/main/cpp/libmobi/.github/workflows/coverity-scan.yml
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
name: coverity-scan
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ public ]
|
||||
|
||||
jobs:
|
||||
coverity-build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: install dependencies
|
||||
run: |
|
||||
sudo apt-get update -qq;
|
||||
sudo apt-get install -y autotools-dev pkg-config automake autoconf libtool;
|
||||
sudo apt-get install -y zlib1g-dev libxml2-dev;
|
||||
- name: download coverity tools
|
||||
run: |
|
||||
curl -Lf \
|
||||
-o cov-analysis-linux64.tar.gz \
|
||||
--form project=bfabiszewski/libmobi \
|
||||
--form token=$TOKEN \
|
||||
https://scan.coverity.com/download/linux64
|
||||
mkdir cov-analysis-linux64
|
||||
tar xzf cov-analysis-linux64.tar.gz --strip 1 -C cov-analysis-linux64
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
- name: autogen
|
||||
run: ./autogen.sh
|
||||
- name: configure
|
||||
run: ./configure ${{ matrix.config.options }}
|
||||
- name: build with cov-build
|
||||
run: |
|
||||
export PATH=`pwd`/cov-analysis-linux64/bin:$PATH
|
||||
cov-build --dir cov-int make -j `nproc`
|
||||
- name: upload results to coverity-scan
|
||||
run: |
|
||||
tar czvf cov-int.tgz cov-int
|
||||
curl -Lf \
|
||||
--form token=$TOKEN \
|
||||
--form email=scan.coverity@fabiszewski.net \
|
||||
--form file=@cov-int.tgz \
|
||||
--form version="`git describe --tags`" \
|
||||
--form description="libmobi `git describe --tags`" \
|
||||
"https://scan.coverity.com/builds?project=bfabiszewski/libmobi"
|
||||
env:
|
||||
TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }}
|
||||
39
app/src/main/cpp/libmobi/.travis.yml
vendored
Normal file
39
app/src/main/cpp/libmobi/.travis.yml
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
os:
|
||||
- linux
|
||||
# - osx
|
||||
language: c
|
||||
compiler:
|
||||
- clang
|
||||
- gcc
|
||||
before_install:
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then
|
||||
sudo apt-get update -qq;
|
||||
sudo apt-get install -y autotools-dev pkg-config automake autoconf libtool;
|
||||
sudo apt-get install -y zlib1g-dev libxml2-dev;
|
||||
elif [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
brew update > /dev/null;
|
||||
brew outdated autoconf || brew upgrade autoconf;
|
||||
brew outdated automake || brew upgrade automake;
|
||||
brew outdated libtool || brew upgrade libtool;
|
||||
fi
|
||||
- git config --global user.name "Travis CI (libmobi)"
|
||||
- git config --global user.email $HOSTNAME":not-for-mail@travis-ci.org"
|
||||
script:
|
||||
- ./autogen.sh
|
||||
- ./configure --enable-debug && make && make test
|
||||
- make clean
|
||||
- ./configure --with-zlib=no --with-libxml2=no && make && make test
|
||||
|
||||
env:
|
||||
global:
|
||||
- secure: "ShIL3IDvH59cJx4QAKWhVTs7ynCAfID11DqK8pIWJX2UtvXc4pdDQUq6U5ZRLUT0BR6kmLNxYrQUpUKvZ9sYnPuj4X5o9jzXzXsJPXTy/qpjiLK4MCZLIlI4OAHexfAuZTOVZHOoE/B8ABpk8nGYUXk02++LxlmwtE/fIWOOWHs="
|
||||
|
||||
addons:
|
||||
coverity_scan:
|
||||
project:
|
||||
name: "bfabiszewski/libmobi"
|
||||
description: "Build submitted via Travis CI"
|
||||
notification_email: scan.coverity@fabiszewski.net
|
||||
build_command_prepend: "./autogen.sh && ./configure"
|
||||
build_command: "make -j 4"
|
||||
branch_pattern: public
|
||||
0
app/src/main/cpp/libmobi/AUTHORS
vendored
Normal file
0
app/src/main/cpp/libmobi/AUTHORS
vendored
Normal file
118
app/src/main/cpp/libmobi/CMakeLists.txt
vendored
Normal file
118
app/src/main/cpp/libmobi/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
# Copyright (c) 2022 Bartek Fabiszewski
|
||||
# http://www.fabiszewski.net
|
||||
#
|
||||
# This file is part of libmobi.
|
||||
# Licensed under LGPL, either version 3, or any later.
|
||||
# See <http://www.gnu.org/licenses/>
|
||||
|
||||
cmake_minimum_required(VERSION 3.12)
|
||||
|
||||
project(LIBMOBI C)
|
||||
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
|
||||
file(STRINGS ${LIBMOBI_SOURCE_DIR}/configure.ac VERSION_LINE REGEX "AC_INIT\\(\\[libmobi\\], \\[(.*)\\]\\)")
|
||||
string(REGEX MATCH "([0-9]+\\.[0-9]+)" PACKAGE_VERSION "${VERSION_LINE}")
|
||||
message(STATUS "libmobi version ${PACKAGE_VERSION}")
|
||||
add_definitions(-DPACKAGE_VERSION="${PACKAGE_VERSION}")
|
||||
string(REPLACE "." ";" VERSION_LIST ${PACKAGE_VERSION})
|
||||
list(GET VERSION_LIST 0 PACKAGE_VERSION_MAJOR)
|
||||
list(GET VERSION_LIST 1 PACKAGE_VERSION_MINOR)
|
||||
|
||||
# Option to enable encryption
|
||||
option(USE_ENCRYPTION "Enable encryption" ON)
|
||||
|
||||
# Option to enable static tools compilation
|
||||
option(TOOLS_STATIC "Enable static tools compilation" OFF)
|
||||
|
||||
# Option to use libxml2
|
||||
option(USE_LIBXML2 "Use libxml2 instead of internal xmlwriter" ON)
|
||||
|
||||
# Option to use zlib
|
||||
option(USE_ZLIB "Use zlib" ON)
|
||||
|
||||
# Option to enable XMLWRITER
|
||||
option(USE_XMLWRITER "Enable xmlwriter (for opf support)" ON)
|
||||
|
||||
# Option to enable debug
|
||||
option(MOBI_DEBUG "Enable debug" OFF)
|
||||
|
||||
# Option to enable debug alloc
|
||||
option(MOBI_DEBUG_ALLOC "Enable debug alloc" OFF)
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build using shared libraries" ON)
|
||||
|
||||
if(TOOLS_STATIC)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
endif(TOOLS_STATIC)
|
||||
|
||||
if(USE_ENCRYPTION)
|
||||
add_definitions(-DUSE_ENCRYPTION)
|
||||
endif(USE_ENCRYPTION)
|
||||
|
||||
if(USE_XMLWRITER)
|
||||
add_definitions(-DUSE_XMLWRITER)
|
||||
if(USE_LIBXML2)
|
||||
add_definitions(-DUSE_LIBXML2)
|
||||
find_package(LibXml2 REQUIRED)
|
||||
include_directories(${LIBXML2_INCLUDE_DIR})
|
||||
endif(USE_LIBXML2)
|
||||
endif(USE_XMLWRITER)
|
||||
|
||||
if(MOBI_DEBUG)
|
||||
add_definitions(-DMOBI_DEBUG)
|
||||
message(STATUS "CMAKE_CXX_COMPILER_ID=${CMAKE_C_COMPILER_ID}")
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "Clang|GNU")
|
||||
add_compile_options(-pedantic -Wall -Wextra -Werror)
|
||||
endif()
|
||||
endif(MOBI_DEBUG)
|
||||
|
||||
if(MOBI_DEBUG_ALLOC)
|
||||
add_definitions(-DMOBI_DEBUG_ALLOC)
|
||||
endif(MOBI_DEBUG_ALLOC)
|
||||
|
||||
if(USE_ZLIB)
|
||||
find_package(ZLIB REQUIRED)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
else()
|
||||
add_definitions(-DUSE_MINIZ)
|
||||
endif(USE_ZLIB)
|
||||
|
||||
include(CheckIncludeFile)
|
||||
include(CheckFunctionExists)
|
||||
check_include_file(unistd.h HAVE_UNISTD_H)
|
||||
if(HAVE_UNISTD_H)
|
||||
add_definitions(-DHAVE_UNISTD_H)
|
||||
endif(HAVE_UNISTD_H)
|
||||
check_function_exists(getopt HAVE_GETOPT)
|
||||
if(HAVE_GETOPT)
|
||||
add_definitions(-DHAVE_GETOPT)
|
||||
endif(HAVE_GETOPT)
|
||||
|
||||
check_function_exists(strdup HAVE_STRDUP)
|
||||
if(HAVE_STRDUP)
|
||||
add_definitions(-DHAVE_STRDUP)
|
||||
endif(HAVE_STRDUP)
|
||||
|
||||
check_include_file(sys/resource.h HAVE_SYS_RESOURCE_H)
|
||||
if(HAVE_SYS_RESOURCE_H)
|
||||
add_definitions(-DHAVE_SYS_RESOURCE_H)
|
||||
endif(HAVE_SYS_RESOURCE_H)
|
||||
|
||||
|
||||
include(CheckCSourceCompiles)
|
||||
foreach(keyword "inline" "__inline__" "__inline")
|
||||
check_c_source_compiles("${keyword} void func(); void func() { } int main() { func(); return 0; }" HAVE_INLINE)
|
||||
if(HAVE_INLINE)
|
||||
add_definitions(-DMOBI_INLINE=${keyword})
|
||||
break()
|
||||
endif(HAVE_INLINE)
|
||||
endforeach(keyword)
|
||||
|
||||
check_c_source_compiles("void func() { } __attribute__((noreturn)); int main() { func(); return 0; }" HAVE_ATTRIBUTE_NORETURN)
|
||||
if(HAVE_ATTRIBUTE_NORETURN)
|
||||
add_definitions(-DHAVE_ATTRIBUTE_NORETURN)
|
||||
endif(HAVE_ATTRIBUTE_NORETURN)
|
||||
|
||||
add_subdirectory(src)
|
||||
# add_subdirectory(tools)
|
||||
165
app/src/main/cpp/libmobi/COPYING
vendored
Normal file
165
app/src/main/cpp/libmobi/COPYING
vendored
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
|
||||
This version of the GNU Lesser General Public License incorporates
|
||||
the terms and conditions of version 3 of the GNU General Public
|
||||
License, supplemented by the additional permissions listed below.
|
||||
|
||||
0. Additional Definitions.
|
||||
|
||||
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||
General Public License.
|
||||
|
||||
"The Library" refers to a covered work governed by this License,
|
||||
other than an Application or a Combined Work as defined below.
|
||||
|
||||
An "Application" is any work that makes use of an interface provided
|
||||
by the Library, but which is not otherwise based on the Library.
|
||||
Defining a subclass of a class defined by the Library is deemed a mode
|
||||
of using an interface provided by the Library.
|
||||
|
||||
A "Combined Work" is a work produced by combining or linking an
|
||||
Application with the Library. The particular version of the Library
|
||||
with which the Combined Work was made is also called the "Linked
|
||||
Version".
|
||||
|
||||
The "Minimal Corresponding Source" for a Combined Work means the
|
||||
Corresponding Source for the Combined Work, excluding any source code
|
||||
for portions of the Combined Work that, considered in isolation, are
|
||||
based on the Application, and not on the Linked Version.
|
||||
|
||||
The "Corresponding Application Code" for a Combined Work means the
|
||||
object code and/or source code for the Application, including any data
|
||||
and utility programs needed for reproducing the Combined Work from the
|
||||
Application, but excluding the System Libraries of the Combined Work.
|
||||
|
||||
1. Exception to Section 3 of the GNU GPL.
|
||||
|
||||
You may convey a covered work under sections 3 and 4 of this License
|
||||
without being bound by section 3 of the GNU GPL.
|
||||
|
||||
2. Conveying Modified Versions.
|
||||
|
||||
If you modify a copy of the Library, and, in your modifications, a
|
||||
facility refers to a function or data to be supplied by an Application
|
||||
that uses the facility (other than as an argument passed when the
|
||||
facility is invoked), then you may convey a copy of the modified
|
||||
version:
|
||||
|
||||
a) under this License, provided that you make a good faith effort to
|
||||
ensure that, in the event an Application does not supply the
|
||||
function or data, the facility still operates, and performs
|
||||
whatever part of its purpose remains meaningful, or
|
||||
|
||||
b) under the GNU GPL, with none of the additional permissions of
|
||||
this License applicable to that copy.
|
||||
|
||||
3. Object Code Incorporating Material from Library Header Files.
|
||||
|
||||
The object code form of an Application may incorporate material from
|
||||
a header file that is part of the Library. You may convey such object
|
||||
code under terms of your choice, provided that, if the incorporated
|
||||
material is not limited to numerical parameters, data structure
|
||||
layouts and accessors, or small macros, inline functions and templates
|
||||
(ten or fewer lines in length), you do both of the following:
|
||||
|
||||
a) Give prominent notice with each copy of the object code that the
|
||||
Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
4. Combined Works.
|
||||
|
||||
You may convey a Combined Work under terms of your choice that,
|
||||
taken together, effectively do not restrict modification of the
|
||||
portions of the Library contained in the Combined Work and reverse
|
||||
engineering for debugging such modifications, if you also do each of
|
||||
the following:
|
||||
|
||||
a) Give prominent notice with each copy of the Combined Work that
|
||||
the Library is used in it and that the Library and its use are
|
||||
covered by this License.
|
||||
|
||||
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||
document.
|
||||
|
||||
c) For a Combined Work that displays copyright notices during
|
||||
execution, include the copyright notice for the Library among
|
||||
these notices, as well as a reference directing the user to the
|
||||
copies of the GNU GPL and this license document.
|
||||
|
||||
d) Do one of the following:
|
||||
|
||||
0) Convey the Minimal Corresponding Source under the terms of this
|
||||
License, and the Corresponding Application Code in a form
|
||||
suitable for, and under terms that permit, the user to
|
||||
recombine or relink the Application with a modified version of
|
||||
the Linked Version to produce a modified Combined Work, in the
|
||||
manner specified by section 6 of the GNU GPL for conveying
|
||||
Corresponding Source.
|
||||
|
||||
1) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (a) uses at run time
|
||||
a copy of the Library already present on the user's computer
|
||||
system, and (b) will operate properly with a modified version
|
||||
of the Library that is interface-compatible with the Linked
|
||||
Version.
|
||||
|
||||
e) Provide Installation Information, but only if you would otherwise
|
||||
be required to provide such information under section 6 of the
|
||||
GNU GPL, and only to the extent that such information is
|
||||
necessary to install and execute a modified version of the
|
||||
Combined Work produced by recombining or relinking the
|
||||
Application with a modified version of the Linked Version. (If
|
||||
you use option 4d0, the Installation Information must accompany
|
||||
the Minimal Corresponding Source and Corresponding Application
|
||||
Code. If you use option 4d1, you must provide the Installation
|
||||
Information in the manner specified by section 6 of the GNU GPL
|
||||
for conveying Corresponding Source.)
|
||||
|
||||
5. Combined Libraries.
|
||||
|
||||
You may place library facilities that are a work based on the
|
||||
Library side by side in a single library together with other library
|
||||
facilities that are not Applications and are not covered by this
|
||||
License, and convey such a combined library under terms of your
|
||||
choice, if you do both of the following:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work based
|
||||
on the Library, uncombined with any other library facilities,
|
||||
conveyed under the terms of this License.
|
||||
|
||||
b) Give prominent notice with the combined library that part of it
|
||||
is a work based on the Library, and explaining where to find the
|
||||
accompanying uncombined form of the same work.
|
||||
|
||||
6. Revised Versions of the GNU Lesser General Public License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions
|
||||
of the GNU Lesser General Public License from time to time. Such new
|
||||
versions will be similar in spirit to the present version, but may
|
||||
differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Library as you received it specifies that a certain numbered version
|
||||
of the GNU Lesser General Public License "or any later version"
|
||||
applies to it, you have the option of following the terms and
|
||||
conditions either of that published version or of any later version
|
||||
published by the Free Software Foundation. If the Library as you
|
||||
received it does not specify a version number of the GNU Lesser
|
||||
General Public License, you may choose any version of the GNU Lesser
|
||||
General Public License ever published by the Free Software Foundation.
|
||||
|
||||
If the Library as you received it specifies that a proxy can decide
|
||||
whether future versions of the GNU Lesser General Public License shall
|
||||
apply, that proxy's public statement of acceptance of any version is
|
||||
permanent authorization for you to choose that version for the
|
||||
Library.
|
||||
372
app/src/main/cpp/libmobi/ChangeLog
vendored
Normal file
372
app/src/main/cpp/libmobi/ChangeLog
vendored
Normal file
|
|
@ -0,0 +1,372 @@
|
|||
2024-10-29: Update github actions
|
||||
2024-10-29: Fix CMake build, closes #49
|
||||
2024-07-04: Minor rewording in man page
|
||||
2024-06-17: Fix typo
|
||||
2024-06-17: Version 0.12
|
||||
2024-02-04: Fix: missing header with libxml2 >= 2.12
|
||||
2024-02-04: Max index count value is too low for some dictionaries
|
||||
2023-08-10: Fix CMake debug build with MSVC, fixes #46
|
||||
2023-07-11: Clean up unused value
|
||||
2023-07-11: Fix clang warning about missing function prototypes (-Wstrict-prototypes)
|
||||
2023-02-21: Update Xcode project settings
|
||||
2023-02-21: Replace deprecated functions
|
||||
2023-02-21: Try to reconstruct sources even on broken indices
|
||||
2023-02-05: Refactor mobi_buffer_get_varlen_internal to be compatible with other buffer functions. Update documentation with information about mobi_buffer_get_varlen_dec limitation.
|
||||
2022-06-26: Fix undefined behavior with null pointer arithmetics in case of corrupt input
|
||||
2022-05-28: Version 0.11
|
||||
2022-05-27: Fix potential null pointer dereference on corrupt input when inflections CNCX record is not initialized
|
||||
2022-05-23: Fix index entries count
|
||||
2022-05-23: Prevent leak of index entries on corrupt data
|
||||
2022-05-23: Add checks for fragments part in case of corrupt data
|
||||
2022-05-17: Fix potential integer overflow with corrupt data
|
||||
2022-05-05: Fix: index entry label not being zero-terminated with corrupt input
|
||||
2022-05-03: Fix boundary checking error in markup search, that could cause buffer over-read with corrupt input
|
||||
2022-05-02: Fix typo in macro name
|
||||
2022-04-27: Fix undefined behavior when passing null to strdup
|
||||
2022-04-27: Fix wrong boundary checks in inflections parser resulting in stack buffer over-read with corrupt input
|
||||
2022-04-26: Fix text formatting
|
||||
2022-04-26: Fix array boundary check when parsing inflections which could result in buffer over-read with corrupt input
|
||||
2022-04-23: Fix formatting
|
||||
2022-04-23: Fix checking boundary of deobfuscation key which could cause buffer over-read with corrupt data
|
||||
2022-04-23: Fix issue with corrupt data with empty lookup string which could lead to read beyond buffer
|
||||
2022-04-23: Fix faulty checks for array boundary which caused buffer over-read with corrupt input
|
||||
2022-04-23: Fix issue with corrupt files with tagvalues_count = 0 that caused null pointer dereference
|
||||
2022-04-23: Fix issues when mobi_buffer_getpointer returns null. With corrupt data this could lead to out-of-bounds read
|
||||
2022-04-13: Add packaging status [skip ci]
|
||||
2022-04-10: Make random generation return proper error codes
|
||||
2022-04-10: Rewrite randombytes for libmobi
|
||||
2022-04-07: Add libsodium randombytes.c
|
||||
2022-04-10: Fix "fallthrough" spelling
|
||||
2022-04-10: Make declaration match definition
|
||||
2022-04-10: Fix different sign comparison warning
|
||||
2022-04-10: Update Xcode project
|
||||
2022-04-10: Don't run tests if bash is missing
|
||||
2022-04-10: Looking for libxml2, first try pkg-config
|
||||
2022-04-04: Update MSVC project
|
||||
2022-04-02: Add support for GNU/kFreeBSD and GNU/Hurd
|
||||
2022-04-02: Check for inline, noreturn support in CMake
|
||||
2022-03-27: Fix format truncation warning
|
||||
2022-03-21: Version 0.10
|
||||
2022-03-21: Update Xcode project [skip ci]
|
||||
2022-03-21: Add functions for retrieving orthographic index entries
|
||||
2022-02-27: Add basic CMake support
|
||||
2022-02-26: GHA: fetch tags with checkout
|
||||
2022-02-26: Minor refactoring of file path manipulation function
|
||||
2022-02-26: Fix memory handling issues
|
||||
2022-02-26: Add coverity scan workflow
|
||||
2022-02-25: Remove obsolete changelog
|
||||
2022-02-25: Fix md5sum output on Windows
|
||||
2022-02-25: Fix inconsistent separators in path on Windows builds
|
||||
2022-02-25: GHA: fix log paths
|
||||
2022-02-25: GHA: fix workflow syntax
|
||||
2022-02-25: GHA: upload test logs on failure
|
||||
2022-02-24: Fix printf format specifier
|
||||
2022-02-24: Fix sample path in Makefile
|
||||
2022-02-24: Missing autotools in mingw workflow
|
||||
2022-02-24: Windows doesn't accept asterisk in file names
|
||||
2022-02-24: Update workflow, add badge
|
||||
2022-02-24: Add mingw workflow
|
||||
2022-02-24: Fix tests in out-of-tree build
|
||||
2022-02-24: Update man pages
|
||||
2022-02-24: Replace non-portable strptime
|
||||
2022-02-24: Make sure both validity period dates are set
|
||||
2022-02-21: Fix strptime not found on linux build
|
||||
2022-02-21: Add build github action
|
||||
2022-02-21: Update README
|
||||
2022-02-21: Minor code cleanups
|
||||
2022-02-21: Unify boolean and static usage in tools
|
||||
2022-02-21: mobimeta: fix null pointer dereference when parsing malformed option
|
||||
2022-02-18: Add hybrid spit option to mobitool
|
||||
2022-02-18: Update documentation
|
||||
2022-02-18: Test both encrypted hybrid parts
|
||||
2022-02-18: Fix: fast decryption routine fails for non-huffman compression
|
||||
2022-02-18: Fix mobitool serial decryption
|
||||
2022-02-18: Add DRM tests
|
||||
2022-02-17: Fix build with encryption disabled
|
||||
2022-02-17: Update tests samples
|
||||
2022-02-16: Add -h option to tools, update man pages
|
||||
2022-02-16: Update Xcode settings
|
||||
2022-02-16: Restructure, cleanup encryption related code, add mobidrm tool
|
||||
2021-11-19: Improve getopt loop, fix config.h to be accessible from all tools
|
||||
2021-11-10: Update xcode project
|
||||
2021-11-10: Add functions to split hybrid files
|
||||
2021-11-10: Avoid modifying existing records, as caller may keep reference to them
|
||||
2021-11-05: Fix: tests fail if pid contains asterisk
|
||||
2021-11-05: Fix: decryption may fail for some records with standard compression
|
||||
2021-11-05: Replace test samples with self-generated smaller ones
|
||||
2021-11-05: Skip test in case of missing checksums
|
||||
2021-10-20: Version 0.9
|
||||
2021-10-24: Fix out-of-tree build
|
||||
2021-10-22: Fix mingw build, code formatting
|
||||
2021-10-14: Fix gcc format truncation warning
|
||||
2021-10-14: Include autogen.sh in distribution bundle
|
||||
2021-10-14: Create codeql-analysis.yml
|
||||
2021-10-14: Fix autoconf 2.70 warnings, clean up
|
||||
2021-10-14: Build fails with autoconf 2.70
|
||||
2021-10-11: Version 0.8
|
||||
2021-10-11: Update Xcode project
|
||||
2021-10-11: Fix warnings about changed signedness
|
||||
2021-09-18: Fix potential out-of-buffer read while parsing corrupt file, closes #38
|
||||
2021-09-18: Fix potential out-of-buffer read while parsing corrupt file, closes #35, #36
|
||||
2021-09-09: Version 0.7
|
||||
2021-09-09: fix oob write bug inside libmobi
|
||||
2021-06-07: Add reference to brew formula
|
||||
2020-09-02: Fix null pointer dereference in case of broken fragment
|
||||
2020-08-01: Update changelog
|
||||
2020-08-01: Version 0.6
|
||||
2020-07-31: Fix typo
|
||||
2020-07-31: Add Readme to dist package
|
||||
2020-07-31: Remove anchor on truncated link
|
||||
2020-07-31: Fix missing option in man page
|
||||
2020-07-30: Include test samples in dist package
|
||||
2020-07-25: Fix gcc 7+ warnings about implicit fall through and format truncation
|
||||
2020-07-24: Unique names for internal functions to avoid confilicts with static linking
|
||||
2020-06-24: Close file in error branch
|
||||
2020-06-24: Fix static compilation with miniz on gcc
|
||||
2020-06-24: Minor documentation fixes
|
||||
2020-06-23: Version 0.5
|
||||
2020-06-23: mobitool: add dump cover option
|
||||
2020-06-23: Minor documentation improvement
|
||||
2020-06-23: Fix potential buffer over-read
|
||||
2019-03-18: Fix: try also "name" attribute when searching for link anchor tags, closes #24
|
||||
2019-02-22: Add mobi_is_replica function
|
||||
2019-02-22: Fix potential read beyond buffer
|
||||
2019-02-22: Travis migration
|
||||
2018-08-07: Fix: missing items in recreated ncx file
|
||||
2018-06-20: Fix: printf format warning on some gcc versions
|
||||
2018-06-20: Fix: make dist broken by nonexistent header files
|
||||
2018-06-20: VERSION 0.4
|
||||
2018-06-20: Fix: buffer overflow (CVE-2018-11726)
|
||||
2018-06-20: Fix: buffer overflow (CVE-2018-11724)
|
||||
2018-06-20: Fix: read beyond buffer (CVE-2018-11725)
|
||||
2018-06-20: Fix: buffer overflow (mobitool), closes #18
|
||||
2018-06-20: Fix: read beyond buffer with corrupted KF8 Boundary record, closes #19
|
||||
2018-06-20: Fix: read beyond buffer, closes #16, #17
|
||||
2018-06-20: Updated xcode project files
|
||||
2018-04-03: Fix: ncx part was not scanned for links, fixes #12
|
||||
2018-04-02: Fix regression, potential use after free
|
||||
2018-04-02: Skip broken resources, fixes #10
|
||||
2018-03-05: Allow processing zero length text records, fixes #9
|
||||
2017-12-25: Skip broken first resource offset instead of dying
|
||||
2017-12-18: Skip broken links reconstruction instead of dying
|
||||
2017-11-27: Disable travis OS X builds, as they usually time out
|
||||
2017-11-16: Fix: increase max number of dictionary entries per record
|
||||
2017-11-14: Fix for some encrypted documents with palmdoc encoding
|
||||
2017-11-06: Fix: potential null pointer dereference
|
||||
2017-10-16: Manpage cleanup
|
||||
2017-09-27: Update README
|
||||
2017-09-26: Increase maximum length of attribute name and value, closes #5
|
||||
2017-02-26: Remove obsolete files from VS build (closes #3) [ci skip]
|
||||
2016-11-05: Mobitool: use epub extension if extracted source resource is epub
|
||||
2016-06-10: Update docs
|
||||
2016-06-10: Update test files
|
||||
2016-06-10: Fix: out of bounds read in corrupt font resource
|
||||
2016-06-10: Prevent memory leak in case of corrupt font resources
|
||||
2016-06-10: Calculate deobfuscation buffer limit from key length
|
||||
2016-06-10: Fix: USE_LIBXML2 macro was not included from config.h
|
||||
2016-06-10: Fix: USE_LIBXML2 macro was not included from config.h
|
||||
2016-06-09: Fix: memory leak in tools
|
||||
2016-06-09: Fix: potential out of bounds read
|
||||
2016-06-09: Fix: memory leak in internal xmlwriter
|
||||
2016-06-01: Update README
|
||||
2016-05-19: Feature: verify decryption key type
|
||||
2016-05-19: Cleanup converting little endian buffer to 32-bit integer
|
||||
2016-05-19: Feature: check drm expiration dates
|
||||
2016-05-18: Fix: memory leaks in encryption
|
||||
2016-05-18: Fix concurrent autotools builds
|
||||
2016-05-18: use relative path, as $(top_srcdir) fails to be substituted (?)
|
||||
2016-05-18: update vcxproj
|
||||
2016-05-18: Include headers in automake sources
|
||||
2016-05-18: Fix: automake out-of-tree miniz build
|
||||
2016-05-18: Fix: wrongly detected fdst record broke some ancient documents
|
||||
2016-05-18: Fix: improve index header parsing, some old dictionaries might not load
|
||||
2016-05-18: Fix: convert encoding of opf strings from cp1252 indices
|
||||
2016-05-18: Quiet warnings about unused values of wiped variables
|
||||
2016-05-18: Fix: potential memory leak
|
||||
2016-05-18: Fix: wrongly decoded "©" entity
|
||||
2016-05-16: Fix: huffdic decompression fails in case of huge documents
|
||||
2016-05-14: Simplify buffer_init_null() function
|
||||
2016-05-14: Use ARRAYSIZE macro
|
||||
2016-05-14: Feature: calculate pid for decryption from device serial number
|
||||
2016-04-29: Use endian-independent byte swapping
|
||||
2016-04-29: Exclude unused miniz functions from binary
|
||||
2016-04-29: Add SHA-1 routines
|
||||
2016-04-27: Fix miniz.c formatting
|
||||
2016-04-27: Documentation
|
||||
2016-04-20: Update changelog
|
||||
2016-04-20: Fix potential null pointer dereference
|
||||
2016-04-20: Remove useless check
|
||||
2016-04-20: Fix text record size calculation
|
||||
2016-04-20: Fix buffer checking and freeing
|
||||
2016-04-19: Update docs
|
||||
2016-04-19: Update ChangeLog
|
||||
2016-04-19: Fix comparison between signed and unsigned integer
|
||||
2016-04-19: use strdup on linux/glibc
|
||||
2016-04-19: Add initial write and metadata editing support. Add mobimeta tool.
|
||||
2016-04-19: Always check whether memory allocation succeeded
|
||||
2016-04-18: Fix: guarantee array resize step is at least 1
|
||||
2016-04-13: Workaround to read some old mobipocket files
|
||||
2016-04-13: Improve pdb dates resolving
|
||||
2016-04-07: Minor documentation edit
|
||||
2016-04-07: Update changelog
|
||||
2016-04-06: Fix format warning
|
||||
2016-04-06: Update test checksums
|
||||
2016-04-06: Fix: <dc:date> "event" attribute needs "opf" namespace
|
||||
2016-04-06: Fix: id attributes in ncx file should be unique
|
||||
2016-04-06: Store full name in MOBIMobiHeader structure
|
||||
2016-04-05: Fix formatting
|
||||
2016-04-05: Fix signedness warning
|
||||
2016-04-04: Fix potential buffer overflow, closes #2
|
||||
2016-04-04: Fix potential null pointer dereference
|
||||
2016-03-23: Fix signedness warnings
|
||||
2016-03-22: Fix: _mkdir needs direct.h on MinGW
|
||||
2016-03-22: Fix tests on Windows
|
||||
2016-03-22: Fix: palmdoc decompression may fail with zero byte in input buffer
|
||||
2016-03-21: VERSION 03: internal xmlwriter, metadata handling functions, bug fixes
|
||||
2016-03-21: Feature: add helper functions for metadata extraction
|
||||
2016-03-21: Load also kf8 data when only kf7 version is requested
|
||||
2016-03-21: Fix: wrong exth header length check could discard some valid headers
|
||||
2016-03-20: Get rid of extended attributes in release archive on OS X
|
||||
2016-03-19: Mobitool: add descriptive error messages based on libmobi return codes
|
||||
2016-03-04: Add extra length check for CMET record extraction
|
||||
2016-03-04: Always check buffer allocation result
|
||||
2016-03-04: Add functions to extract conversion source and log, also add this feature to mobitool
|
||||
2016-03-04: Remove some stray printfs
|
||||
2016-03-03: Remove not used AC_FUNC_MALLOC/REALLOC macros that break cross-compilation
|
||||
2016-03-03: Fix potential illegal memory access in miniz.c
|
||||
2016-03-03: Fix potential dereference of null pointer in miniz.c
|
||||
2016-03-03: Fix for Android bionic libc bug (SIZE_MAX missing in stdint.h)
|
||||
2016-03-03: Fix mobitool compilation on MSVC++
|
||||
2016-03-03: Add EPUB creation feature to mobitool
|
||||
2016-03-02: Fix potential buffer overflow, null pointer dereference
|
||||
2016-03-02: Add travis test for no-external-dependency build
|
||||
2016-03-02: Fix missing strdup on linux
|
||||
2016-03-02: Add internal xmlwriter (as an alternative to libxml2)
|
||||
2016-03-01: Feature: decode html entities in exth header strings
|
||||
2016-02-29: Fix: potential buffer overflow
|
||||
2016-02-29: Fix: wrong pid calculation (regression introduced in 0.2)
|
||||
2016-02-26: VERSION 0.2: increased stability, lots of bugs fixed
|
||||
2016-02-26: Add Xcode project file
|
||||
2016-02-26: Preliminary support for MSVC++ compiler
|
||||
2016-02-26: Do not use variable length arrays
|
||||
2016-02-26: Refactor mobi_reconstruct_parts() to use MOBIFragment list
|
||||
2016-02-26: Fix compiler warning about sign conversion
|
||||
2016-02-26: Fix compiler warning about type conversion
|
||||
2016-02-26: Check the result of malloc/calloc
|
||||
2016-02-26: Fix inconsistent use of const between some definitions and declarations
|
||||
2016-02-24: Fix inconsistence between function declaration and definition
|
||||
2016-02-24: Fix various potential crashes in case of corrupt input (afl-fuzz)
|
||||
2016-02-24: Fix dead code warnings in miniz
|
||||
2015-11-26: Export mobi_get_first_resource_record() function
|
||||
2015-11-26: Fix: double free on corrupt cdic
|
||||
2015-11-02: Update docs
|
||||
2015-11-02: Feature: add helper functions to find resources by flow id
|
||||
2015-11-02: Feature: export MOBI_NOTSET macro
|
||||
2015-11-02: Feature: give more options to parse rawml function
|
||||
2015-10-24: Restore travis.yml
|
||||
2015-10-24: Fix OSX travis build
|
||||
2015-10-24: Fix OSX travis build
|
||||
2015-10-24: Fix multiline inline script
|
||||
2015-10-24: Enable multi-OS feature
|
||||
2015-10-24: Fix: unique temporary name for parallel tests
|
||||
2015-10-24: Fix: decoding video resources falsely reported as failed
|
||||
2015-10-24: Fix: tests, some md5sum implementations insert double spaces
|
||||
2015-10-24: Fix for automake < 1.13
|
||||
2015-10-23: Add simple tests framework
|
||||
2015-10-23: Fix: increase max index entries per record count, as some rare samples fail
|
||||
2015-10-22: Fix: incorrectly decoded video/audio resources
|
||||
2015-10-22: Feature: add option to specify output path
|
||||
2015-10-14: Add some internal functions to public API: mobi_get_flow_by_uid, mobi_get_resource_by_uid, mobi_get_part_by_uid, mobi_get_exthrecord_by_tag
|
||||
2015-06-13: update changelog
|
||||
2015-06-13: fix: various invalid memory access
|
||||
2015-06-13: don't quit on invalid input, instead substitute with replacement character
|
||||
2015-06-12: fix typo
|
||||
2015-06-12: update changelog
|
||||
2015-06-12: fix: reconstruction failed when there were gaps between fragments
|
||||
2015-06-12: add EXTH tags
|
||||
2015-06-12: prevent return of garbage value check return value in case of failed malloc
|
||||
2015-06-12: fix invalid memory access
|
||||
2015-04-12: Fix reconstruction of "kindle:embed" links without mime type (regression)
|
||||
2015-04-12: Add sanity checks to link reconstruction functions, allow skipping some malformed patterns
|
||||
2015-04-12: Fix infinite loop in guide build while unknown tag was found
|
||||
2015-04-12: Increase max recursion level for huffman decompression
|
||||
2015-03-28: update docs
|
||||
2015-03-28: fix solaris studio compiler warnings
|
||||
2015-03-28: fix solaris studio compiler build
|
||||
2015-02-18: Fix "more than one: -compatibility_version specified" error on powerpc
|
||||
2014-11-24: improve docs
|
||||
2014-11-24: simplify public header
|
||||
2014-11-21: changelog update [ci skip]
|
||||
2014-11-21: README
|
||||
2014-11-21: fix: add sanity checks
|
||||
2014-11-21: Fix: add sanity checks
|
||||
2014-11-21: add sanity check to huffcdic indices count
|
||||
2014-11-21: fix number of leaks and other minor issues (by coverity scan)
|
||||
2014-11-20: missing notification email kills coverity scan
|
||||
2014-11-20: update travis.yml
|
||||
2014-11-20: upgrade travis.ml with covert scan
|
||||
2014-11-20: update README.md
|
||||
2014-11-20: add .travis.yml
|
||||
2014-11-20: update REAME.md
|
||||
2014-11-20: update README.md
|
||||
2014-11-20: update docs
|
||||
2014-11-20: feature: add decryption support
|
||||
2014-11-20: mkdir cleanup
|
||||
2014-11-17: documentation
|
||||
2014-11-17: strip unneeded <aid/> tags
|
||||
2014-11-16: fix: potential leak
|
||||
2014-11-16: fix: regression, some image tags were not reconstructed
|
||||
2014-11-16: fix: improve ligatures handling
|
||||
2014-11-16: override darwin linker default versioning
|
||||
2014-11-15: fix: get proper LIGT entries count from index header
|
||||
2014-11-15: feature: unpack records into new folder
|
||||
2014-11-14: make README readable on github
|
||||
2014-11-14: add README for mobitool
|
||||
2014-11-14: fix: dictionaries with large inflection rules failed
|
||||
2014-11-14: feature: support encoded ligatures in index entry labels
|
||||
2014-11-14: readme
|
||||
2014-11-14: update changelog
|
||||
2014-11-13: feature: support for older inflections scheme
|
||||
2014-11-13: bug: files with short tagx header won't open
|
||||
2014-11-13: cleanup unneeded include
|
||||
2014-11-13: use strdup on linux/glibc
|
||||
2014-11-13: debugging cleanup
|
||||
2014-11-13: reorganize source files
|
||||
2014-11-13: use strdup on linux/glibc
|
||||
2014-11-11: update changelog
|
||||
2014-11-11: update changeling
|
||||
2014-11-11: fix: documents with text record size > 4096 failed to load
|
||||
2014-11-11: add: function to decode flat index entries
|
||||
2014-11-11: debug: add functions for debugging indices
|
||||
2014-11-11: cleanup
|
||||
2014-11-11: fix: variable length value wrongly calculated when going backwards
|
||||
2014-11-08: update documentation
|
||||
2014-11-08: update changelog
|
||||
2014-11-08: add support for reconstructing inflections index entries
|
||||
2014-11-08: parsing of exth header failed in some cases
|
||||
2014-11-08: fix: some links reconstruction in kf7 failed
|
||||
2014-11-08: improve debug info
|
||||
2014-11-08: failed malloc false reports
|
||||
2014-11-03: fix problem with uncompressed documents
|
||||
2014-11-03: fix broken locales
|
||||
2014-11-03: remove obsolete includes
|
||||
2014-11-03: git log > changelog
|
||||
2014-11-03: improved buffer handling
|
||||
2014-11-03: improved OPF for dictionaries
|
||||
2014-11-03: proper rawml->orth initialization and freeing
|
||||
2014-11-03: fix subject field in opf
|
||||
2014-11-03: handle UTF-16 surrogates, make ORDT lookups locale independent
|
||||
2014-11-01: move dict reconstruction to separate function
|
||||
2014-11-01: cleanup
|
||||
2014-11-01: quiet gcc warning on printf format
|
||||
2014-11-01: reconstruction of orth dictionary entries
|
||||
2014-09-27: use mobi_list_del_all()
|
||||
2014-09-25: postpone conversion to utf8 after all source reconstructions
|
||||
2014-09-24: comment
|
||||
2014-09-24: comments
|
||||
2014-09-12: doxygen comment
|
||||
2014-09-12: data size in comment
|
||||
2014-09-05: MOBIArray data type fix
|
||||
2014-09-05: config.h fixes
|
||||
2014-06-29: merge master
|
||||
2014-04-11: initial commit
|
||||
12
app/src/main/cpp/libmobi/Makefile.am
vendored
Normal file
12
app/src/main/cpp/libmobi/Makefile.am
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# project Makefile.am
|
||||
|
||||
SUBDIRS = src tools tests
|
||||
|
||||
EXTRA_DIST = README.md autogen.sh
|
||||
|
||||
test: check
|
||||
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
pkgconfig_DATA = libmobi.pc
|
||||
0
app/src/main/cpp/libmobi/NEWS
vendored
Normal file
0
app/src/main/cpp/libmobi/NEWS
vendored
Normal file
142
app/src/main/cpp/libmobi/README.md
vendored
Normal file
142
app/src/main/cpp/libmobi/README.md
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# Libmobi
|
||||
|
||||
C library for handling Mobipocket/Kindle (MOBI) ebook format documents.
|
||||
|
||||
Library comes with several [command line tools](https://github.com/bfabiszewski/libmobi/tree/public/tools) for working with mobi ebooks.
|
||||
The tools source may also be used as an example on how to use the library.
|
||||
|
||||
## Features:
|
||||
- reading and parsing:
|
||||
- some older text Palmdoc formats (pdb),
|
||||
- Mobipocket files (prc, mobi),
|
||||
- newer MOBI files including KF8 format (azw, azw3),
|
||||
- Replica Print files (azw4)
|
||||
- recreating source files using indices
|
||||
- reconstructing references (links and embedded) in html files
|
||||
- reconstructing source structure that can be fed back to kindlegen
|
||||
- reconstructing dictionary markup (orth, infl tags)
|
||||
- writing back loaded documents
|
||||
- metadata editing
|
||||
- handling encrypted documents
|
||||
- encrypting documents for use on eInk Kindles
|
||||
|
||||
## Todo:
|
||||
- improve writing
|
||||
- serialize rawml into raw records
|
||||
- process RESC records
|
||||
|
||||
## Doxygen documentation:
|
||||
- [functions](http://www.fabiszewski.net/libmobi/group__mobi__export.html),
|
||||
- [structures for the raw, unparsed records metadata and data](http://www.fabiszewski.net/libmobi/group__raw__structs.html),
|
||||
- [structures for the parsed records metadata and data](http://www.fabiszewski.net/libmobi/group__parsed__structs.html),
|
||||
- [enums](http://www.fabiszewski.net/libmobi/group__mobi__enums.html)
|
||||
|
||||
## Source:
|
||||
- [on github](https://github.com/bfabiszewski/libmobi/)
|
||||
|
||||
## Packages:
|
||||
[](https://repology.org/project/libmobi/versions)
|
||||
|
||||
## Installation:
|
||||
|
||||
[for git] $ ./autogen.sh
|
||||
$ ./configure
|
||||
$ make
|
||||
[optionally] $ make test
|
||||
$ sudo make install
|
||||
|
||||
On macOS, you can install via [Homebrew](https://brew.sh/) with `brew install libmobi`.
|
||||
|
||||
## Alternative build systems
|
||||
- The supported way of building project is by using autotools.
|
||||
- Optionally project provides basic support for CMake, Xcode and MSVC++ systems. However these alternative configurations are not covering all options of autotools project. They are also not tested and not updated regularly.
|
||||
|
||||
## Usage
|
||||
- single include file: `#include <mobi.h>`
|
||||
- linker flag: `-lmobi`
|
||||
- basic usage:
|
||||
```c
|
||||
#include <mobi.h>
|
||||
|
||||
/* Initialize main MOBIData structure */
|
||||
/* Must be deallocated with mobi_free() when not needed */
|
||||
MOBIData *m = mobi_init();
|
||||
if (m == NULL) {
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Open file for reading */
|
||||
FILE *file = fopen(fullpath, "rb");
|
||||
if (file == NULL) {
|
||||
mobi_free(m);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Load file into MOBIData structure */
|
||||
/* This structure will hold raw data/metadata from mobi document */
|
||||
MOBI_RET mobi_ret = mobi_load_file(m, file);
|
||||
fclose(file);
|
||||
if (mobi_ret != MOBI_SUCCESS) {
|
||||
mobi_free(m);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Initialize MOBIRawml structure */
|
||||
/* Must be deallocated with mobi_free_rawml() when not needed */
|
||||
/* In the next step this structure will be filled with parsed data */
|
||||
MOBIRawml *rawml = mobi_init_rawml(m);
|
||||
if (rawml == NULL) {
|
||||
mobi_free(m);
|
||||
return ERROR;
|
||||
}
|
||||
/* Raw data from MOBIData will be converted to html, css, fonts, media resources */
|
||||
/* Parsed data will be available in MOBIRawml structure */
|
||||
mobi_ret = mobi_parse_rawml(rawml, m);
|
||||
if (mobi_ret != MOBI_SUCCESS) {
|
||||
mobi_free(m);
|
||||
mobi_free_rawml(rawml);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Do something useful here */
|
||||
/* ... */
|
||||
/* For examples how to access data in MOBIRawml structure see mobitool.c */
|
||||
|
||||
/* Free MOBIRawml structure */
|
||||
mobi_free_rawml(rawml);
|
||||
|
||||
/* Free MOBIData structure */
|
||||
mobi_free(m);
|
||||
|
||||
return SUCCESS;
|
||||
```
|
||||
- for examples of usage, see [tools](https://github.com/bfabiszewski/libmobi/tree/public/tools)
|
||||
|
||||
|
||||
## Requirements
|
||||
- compiler supporting C99
|
||||
- zlib (optional, configure --with-zlib=no to use included miniz.c instead)
|
||||
- libxml2 (optional, configure --with-libxml2=no to use internal xmlwriter)
|
||||
- tested with gcc (>=4.2.4), clang (llvm >=3.4), sun c (>=5.13), MSVC++ (2015)
|
||||
- builds on Linux, MacOS, Windows (MSVC++, MinGW), Android, Solaris
|
||||
- tested architectures: x86, x86-64, arm, ppc
|
||||
- works cross-compiled on Kindle :)
|
||||
|
||||
## Tests
|
||||
- [](https://github.com/bfabiszewski/libmobi/actions)
|
||||
- [](https://travis-ci.com/bfabiszewski/libmobi)
|
||||
- [](https://scan.coverity.com/projects/3521)
|
||||
|
||||
## Projects using libmobi
|
||||
- [KyBook 2 Reader](http://kybook-reader.com)
|
||||
- [@Voice Aloud Reader](http://www.hyperionics.com/atVoice/)
|
||||
- [QLMobi quicklook plugin](https://github.com/bfabiszewski/QLMobi/tree/master/QLMobi)
|
||||
- [Librera Reader](http://librera.mobi)
|
||||
- ... (let me know to include your project)
|
||||
|
||||
## License:
|
||||
- LGPL, either version 3, or any later
|
||||
|
||||
## Credits:
|
||||
- The huffman decompression and KF8 parsing algorithms were learned by studying python source code of [KindleUnpack](https://github.com/kevinhendricks/KindleUnpack).
|
||||
- Thanks to all contributors of Mobileread [MOBI wiki](http://wiki.mobileread.com/wiki/MOBI)
|
||||
3
app/src/main/cpp/libmobi/autogen.sh
vendored
Normal file
3
app/src/main/cpp/libmobi/autogen.sh
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
mkdir -p m4 && \
|
||||
autoreconf --force --install -I m4
|
||||
419
app/src/main/cpp/libmobi/configure.ac
vendored
Normal file
419
app/src/main/cpp/libmobi/configure.ac
vendored
Normal file
|
|
@ -0,0 +1,419 @@
|
|||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ([2.62])
|
||||
AC_INIT([libmobi], [0.12])
|
||||
AC_CONFIG_SRCDIR([src/buffer.c])
|
||||
|
||||
# Enable automake
|
||||
AM_INIT_AUTOMAKE([1.11 -Wall foreign subdir-objects])
|
||||
# all defined C macros (HAVE_*) will be saved to this file
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# Checks for programs.
|
||||
AC_PROG_CC
|
||||
m4_version_prereq([2.70], [], [AC_PROG_CC_C99])
|
||||
AM_PROG_CC_C_O
|
||||
AC_PROG_INSTALL
|
||||
m4_ifdef([AM_PROG_AR], [AM_PROG_AR])
|
||||
|
||||
# Init libtool
|
||||
m4_ifdef([LT_INIT], [LT_INIT], [AC_PROG_LIBTOOL])
|
||||
|
||||
# Checks for libraries.
|
||||
|
||||
# Checks for header files.
|
||||
AC_HEADER_STDBOOL
|
||||
AC_CHECK_HEADERS([stdlib.h string.h utime.h unistd.h sys/resource.h])
|
||||
|
||||
# Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_INT32_T
|
||||
AC_TYPE_INT64_T
|
||||
AC_TYPE_INT8_T
|
||||
AC_TYPE_SIZE_T
|
||||
AC_TYPE_UINT16_T
|
||||
AC_TYPE_UINT32_T
|
||||
AC_TYPE_UINT64_T
|
||||
AC_TYPE_UINT8_T
|
||||
|
||||
# Checks for library functions.
|
||||
AC_FUNC_MKTIME
|
||||
AC_CHECK_FUNCS([memmove memset mkdir strdup strpbrk strrchr strstr strtoul utime])
|
||||
|
||||
# check for getopt() function
|
||||
AC_MSG_CHECKING([for getopt])
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="-Werror"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#if HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif]],
|
||||
[[return getopt(0, NULL, NULL);]])],
|
||||
[have_getopt=yes
|
||||
AC_DEFINE([HAVE_GETOPT], [1], [Define whether getopt() function is available])],
|
||||
[have_getopt=no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AC_MSG_RESULT([$have_getopt])
|
||||
AM_CONDITIONAL([USE_INTERNAL_GETOPT], [test x$have_getopt = xno])
|
||||
|
||||
# Check for oracle solaris studio c compiler
|
||||
AC_CHECK_DECL([__SUNPRO_C], [SUNCC=yes], [SUNCC=no])
|
||||
|
||||
# Get rid of extended attributes in release archive on macOS
|
||||
case "$host" in
|
||||
*-*-darwin*)
|
||||
am__tar="COPY_EXTENDED_ATTRIBUTES_DISABLE=1 COPYFILE_DISABLE=1 ${am__tar}"
|
||||
esac
|
||||
|
||||
# Check for -fvisibility=hidden to determine if we can do GNU-style
|
||||
# visibility attributes for symbol export control
|
||||
AC_MSG_CHECKING([for visibility hidden compiler flag])
|
||||
VISIBILITY_HIDDEN=
|
||||
if test x$SUNCC = xyes; then
|
||||
# check if we can use -xldscope=hidden
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="-xldscope=hidden"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]], [[]])],
|
||||
[enable_fvisibility_hidden=yes],
|
||||
[enable_fvisibility_hidden=no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
|
||||
AS_IF([test x$enable_fvisibility_hidden = xyes], [VISIBILITY_HIDDEN="-xldscope=hidden"])
|
||||
else
|
||||
case "$host" in
|
||||
*-*-mingw*)
|
||||
# on mingw32 we do -fvisibility=hidden and __declspec(dllexport)
|
||||
VISIBILITY_HIDDEN="-fvisibility=hidden"
|
||||
;;
|
||||
*)
|
||||
# on other compilers, check if we can do -fvisibility=hidden
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="-fvisibility=hidden -Werror"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]], [[]])],
|
||||
[enable_fvisibility_hidden=yes],
|
||||
[enable_fvisibility_hidden=no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
|
||||
AS_IF([test x$enable_fvisibility_hidden = xyes], [VISIBILITY_HIDDEN="-fvisibility=hidden"])
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
AC_MSG_RESULT([$VISIBILITY_HIDDEN])
|
||||
AC_SUBST([VISIBILITY_HIDDEN])
|
||||
|
||||
# MinGW seems to need this
|
||||
case "$host" in
|
||||
*-*-mingw*)
|
||||
NO_UNDEFINED="-no-undefined"
|
||||
AVOID_VERSION="-avoid-version"
|
||||
ISO99_SOURCE="-D_ISOC99_SOURCE=1"
|
||||
WIN32=yes
|
||||
;;
|
||||
*)
|
||||
NO_UNDEFINED=
|
||||
AVOID_VERSION=
|
||||
ISO99_SOURCE=
|
||||
WIN32=no
|
||||
;;
|
||||
esac
|
||||
AC_SUBST([NO_UNDEFINED])
|
||||
AC_SUBST([AVOID_VERSION])
|
||||
AC_SUBST([ISO99_SOURCE])
|
||||
AC_SUBST([WIN32])
|
||||
|
||||
# Override default versioning of Darwin linker
|
||||
case "$host" in
|
||||
*-*-darwin*)
|
||||
case "$host" in
|
||||
# exclude ppc as it breaks linker
|
||||
ppc-* | powerpc-*)
|
||||
DARWIN_LDFLAGS=
|
||||
;;
|
||||
*)
|
||||
MAJOR=`echo "${PACKAGE_VERSION}" | cut -d . -f 1`
|
||||
DARWIN_LDFLAGS="-Wl,-compatibility_version,${MAJOR} -Wl,-current_version,${PACKAGE_VERSION}"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
DARWIN_LDFLAGS=
|
||||
;;
|
||||
esac
|
||||
AC_SUBST([DARWIN_LDFLAGS])
|
||||
|
||||
# Check for --allow-multiple-definition support in linker
|
||||
AC_MSG_CHECKING([whether linker supports --allow-multiple-definition flag])
|
||||
MOBI_ALLOW_MULTIPLE=
|
||||
saved_CFLAGS="$CFLAGS"
|
||||
CFLAGS="-Wl,--allow-multiple-definition -Werror"
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]], [[]])],
|
||||
[def_allow_multiple=yes],
|
||||
[def_allow_multiple=no])
|
||||
CFLAGS="$saved_CFLAGS"
|
||||
AS_IF([test x$def_allow_multiple = xyes], [MOBI_ALLOW_MULTIPLE="-Wl,--allow-multiple-definition"])
|
||||
AC_MSG_RESULT([$def_allow_multiple])
|
||||
AC_SUBST([MOBI_ALLOW_MULTIPLE])
|
||||
|
||||
# Check for non-broken inline under various spellings
|
||||
AC_MSG_CHECKING([for inline keyword])
|
||||
def_inline=""
|
||||
for inline_key in inline __inline__ __inline
|
||||
do
|
||||
AC_COMPILE_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[]],
|
||||
[[} $inline_key int foo() { return 0; } int bar() { return foo();]])],
|
||||
[def_inline=$inline_key; break])
|
||||
done
|
||||
AC_MSG_RESULT([$def_inline])
|
||||
AC_DEFINE_UNQUOTED([MOBI_INLINE], [$def_inline], [How to obtain function inlining.])
|
||||
|
||||
# Check for noreturn attribute support
|
||||
AC_MSG_CHECKING([whether compiler supports noreturn attribute])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM([[]], [[void foo( void ) __attribute__((noreturn));]])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_ATTRIBUTE_NORETURN], [1], [Define to 1 if compiler supports __attribute__((noreturn))])],
|
||||
[AC_MSG_RESULT([no])]
|
||||
)
|
||||
|
||||
# Check --enable-xmlwriter
|
||||
XMLWRITER_OPT=""
|
||||
AC_MSG_CHECKING([whether enable xmlwriter (for opf support)])
|
||||
AC_ARG_ENABLE(
|
||||
[xmlwriter],
|
||||
[AS_HELP_STRING([--enable-xmlwriter], [enable xmlwriter (for opf support) @<:@default=yes@:>@])],
|
||||
[case "$enableval" in
|
||||
yes) xmlwriter=yes ;;
|
||||
no) xmlwriter=no ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for --enable-xmlwriter]) ;;
|
||||
esac],
|
||||
[xmlwriter=yes])
|
||||
AC_MSG_RESULT([$xmlwriter])
|
||||
AM_CONDITIONAL([USE_XMLWRITER], [test x$xmlwriter = xyes])
|
||||
if test x$xmlwriter = xyes; then
|
||||
AC_DEFINE([USE_XMLWRITER], [1], [Define whether enable xmlwriter (for opf support)])
|
||||
|
||||
# test for --with-libxml2
|
||||
AC_MSG_CHECKING([whether compile with libxml2])
|
||||
AC_ARG_WITH(
|
||||
[libxml2],
|
||||
[AS_HELP_STRING([--with-libxml2], [Use libxml2 instead of internal xmlwriter @<:@default=yes@:>@])],
|
||||
[if test "x$withval" = xyes; then use_libxml2=yes; else use_libxml2=no; fi],
|
||||
[use_libxml2=yes])
|
||||
AC_MSG_RESULT([$use_libxml2])
|
||||
|
||||
if test x$use_libxml2 = xyes; then
|
||||
AC_ARG_VAR([XML2_CONFIG], [path to xml2-config utility])
|
||||
AC_CHECK_PROGS([XML2_CONFIG], [xml2-config])
|
||||
AC_CHECK_PROGS([PKG_CONFIG], [pkg-config])
|
||||
AC_MSG_CHECKING([for libxml2 path supplier])
|
||||
if test -n "$PKG_CONFIG" && $PKG_CONFIG --exists libxml-2.0; then
|
||||
LIBXML2_CFLAGS="`$PKG_CONFIG --cflags libxml-2.0`"
|
||||
LIBXML2_LDFLAGS="`$PKG_CONFIG --libs libxml-2.0`"
|
||||
AC_MSG_RESULT([pkg-config])
|
||||
elif test -n "$XML2_CONFIG"; then
|
||||
LIBXML2_CFLAGS="`$XML2_CONFIG --cflags`"
|
||||
LIBXML2_LDFLAGS="`$XML2_CONFIG --libs`"
|
||||
AC_MSG_RESULT([xml2-config])
|
||||
else
|
||||
LIBXML2_CFLAGS=-I/usr/include/libxml2
|
||||
LIBXML2_LDFLAGS=-lxml2
|
||||
AC_MSG_RESULT([generic])
|
||||
fi
|
||||
saved_CPPFLAGS=$CPPFLAGS
|
||||
CPPFLAGS="$CPPFLAGS $LIBXML2_CFLAGS"
|
||||
AC_CHECK_HEADER(
|
||||
[libxml/xmlwriter.h],
|
||||
[AC_DEFINE([USE_LIBXML2], [1], [Define if you want to use libxml2 library])],
|
||||
[AC_MSG_ERROR([couldn't find libxml2])])
|
||||
CPPFLAGS=$saved_CPPFLAGS
|
||||
else
|
||||
LIBXML2_LDFLAGS=
|
||||
LIBXML2_CFLAGS=
|
||||
fi
|
||||
AC_SUBST([LIBXML2_LDFLAGS])
|
||||
AC_SUBST([LIBXML2_CFLAGS])
|
||||
XMLWRITER_OPT="yes"
|
||||
fi
|
||||
AM_CONDITIONAL([USE_LIBXML2], [test x$use_libxml2 = xyes])
|
||||
AC_SUBST([XMLWRITER_OPT])
|
||||
|
||||
# Check --enable-encryption
|
||||
ENCRYPTION_OPT=""
|
||||
AC_MSG_CHECKING([whether enable encryption])
|
||||
AC_ARG_ENABLE(
|
||||
[encryption],
|
||||
[AS_HELP_STRING([--enable-encryption], [enable encryption @<:@default=yes@:>@])],
|
||||
[case "$enableval" in
|
||||
yes) encryption=yes ;;
|
||||
no) encryption=no ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for --enable-encryption]) ;;
|
||||
esac],
|
||||
[encryption=yes])
|
||||
AC_MSG_RESULT([$encryption])
|
||||
AM_CONDITIONAL([USE_ENCRYPTION], [test x$encryption = xyes])
|
||||
if test x$encryption = xyes; then
|
||||
AC_DEFINE([USE_ENCRYPTION], [1], [Enable encryption])
|
||||
ENCRYPTION_OPT="yes"
|
||||
|
||||
AC_CHECK_HEADERS([sys/random.h])
|
||||
AC_MSG_CHECKING([for getrandom with a standard API])
|
||||
AC_LINK_IFELSE(
|
||||
[AC_LANG_PROGRAM(
|
||||
[[#include <stdlib.h>
|
||||
#ifdef HAVE_UNISTD_H
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_RANDOM_H
|
||||
# include <sys/random.h>
|
||||
#endif]],
|
||||
[[unsigned char buf;
|
||||
if (&getrandom != NULL) {
|
||||
(void) getrandom((void *) &buf, 1U, 0U);
|
||||
}]])],
|
||||
[AC_MSG_RESULT([yes])
|
||||
AC_CHECK_FUNCS([getrandom])],
|
||||
[AC_MSG_RESULT([no])])
|
||||
|
||||
fi
|
||||
AC_SUBST([ENCRYPTION_OPT])
|
||||
|
||||
# Check --enable-debug
|
||||
AC_MSG_CHECKING([whether enable debugging])
|
||||
AC_ARG_ENABLE(
|
||||
[debug],
|
||||
[AS_HELP_STRING([--enable-debug], [enable debugging @<:@default=no@:>@])],
|
||||
[case "$enableval" in
|
||||
yes) debug=yes ;;
|
||||
no) debug=no ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for --enable-debug]) ;;
|
||||
esac],
|
||||
[debug=no])
|
||||
AC_MSG_RESULT([$debug])
|
||||
|
||||
DEBUG_CFLAGS=
|
||||
if test x$debug = xyes; then
|
||||
AC_DEFINE([MOBI_DEBUG], [1], [Enable debugging])
|
||||
if test x$SUNCC = xyes; then
|
||||
DEBUG_CFLAGS="-v -errwarn"
|
||||
else
|
||||
DEBUG_CFLAGS="-pedantic -Wall -Wextra -Werror"
|
||||
fi
|
||||
fi
|
||||
AC_SUBST([DEBUG_CFLAGS])
|
||||
|
||||
# Check --enable-debug-alloc
|
||||
AC_MSG_CHECKING([whether enable alloc debugging])
|
||||
AC_ARG_ENABLE(
|
||||
[debug_alloc],
|
||||
[AS_HELP_STRING([--enable-debug-alloc], [enable memory allocation debugging @<:@default=no@:>@])],
|
||||
[case "$enableval" in
|
||||
yes) debug_alloc=yes ;;
|
||||
no) debug_alloc=no ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for --enable-debug-alloc]) ;;
|
||||
esac],
|
||||
[debug_alloc=no])
|
||||
AC_MSG_RESULT([$debug_alloc])
|
||||
|
||||
if test x$debug_alloc = xyes; then
|
||||
AC_DEFINE([MOBI_DEBUG_ALLOC], [1], [Enable alloc debugging])
|
||||
fi
|
||||
|
||||
# Check --enable-tools-static
|
||||
AC_MSG_CHECKING([whether link tools against static libmobi])
|
||||
AC_ARG_ENABLE(
|
||||
[tools_static],
|
||||
[AS_HELP_STRING([--enable-tools-static], [link tools against static libmobi @<:@default=no@:>@])],
|
||||
[case "$enableval" in
|
||||
yes) tools_static=yes ;;
|
||||
no) tools_static=no ;;
|
||||
*) AC_MSG_ERROR([bad value $enableval for --enable-tools-static]) ;;
|
||||
esac],
|
||||
[tools_static=no])
|
||||
AC_MSG_RESULT([$tools_static])
|
||||
TOOLS_STATIC=
|
||||
if test x$tools_static = xyes; then
|
||||
TOOLS_STATIC="-static"
|
||||
fi
|
||||
AC_SUBST([TOOLS_STATIC])
|
||||
|
||||
# test for --with-zlib
|
||||
AC_MSG_CHECKING([whether compile with zlib])
|
||||
AC_ARG_WITH(
|
||||
[zlib],
|
||||
[AS_HELP_STRING([--with-zlib], [Use zlib instead of included miniz @<:@default=yes@:>@])],
|
||||
[if test "x$withval" = xyes; then use_zlib=yes; else use_zlib=no; fi],
|
||||
[use_zlib=yes])
|
||||
AC_MSG_RESULT([$use_zlib])
|
||||
AM_CONDITIONAL([USE_ZLIB], [test x$use_zlib = xyes])
|
||||
AM_CONDITIONAL([USE_MINIZ], [test x$use_zlib = xno])
|
||||
AM_CONDITIONAL([USE_STATIC], [test x$tools_static = xyes])
|
||||
if test x$use_zlib = xyes; then
|
||||
AC_CHECK_HEADER(
|
||||
[zlib.h],
|
||||
[AC_DEFINE([USE_ZLIB], [1], [Define if you want to use system zlib library])
|
||||
LIBZ_LDFLAGS=-lz
|
||||
MINIZ_CFLAGS=],
|
||||
[AC_MSG_ERROR([couldn't find zlib header])])
|
||||
else
|
||||
AC_DEFINE([USE_MINIZ], [1], [Define if you want to use included miniz library])
|
||||
MINIZ_CFLAGS="-D_POSIX_C_SOURCE=200112L"
|
||||
LIBZ_LDFLAGS=
|
||||
fi
|
||||
AC_SUBST([LIBZ_LDFLAGS])
|
||||
AC_SUBST([MINIZ_CFLAGS])
|
||||
|
||||
# Check for md5 or md5sum program, needed for tests
|
||||
AC_ARG_VAR([MD5PROG], [md5 hashing program executable])
|
||||
AS_IF([test -z "$MD5PROG"], [AC_CHECK_PROG([MD5PROG], [md5sum], [md5sum -t])], [])
|
||||
AS_IF([test -z "$MD5PROG"], [AC_CHECK_PROG([MD5PROG], [md5], [md5 -r])], [])
|
||||
AS_IF([test -z "$MD5PROG"], [AC_MSG_WARN([md5 hashing program not found, some tests will be skipped])], [])
|
||||
AC_PATH_PROG([BASH_PATH], [bash])
|
||||
AS_IF(
|
||||
[test -z "$BASH_PATH"],
|
||||
[AC_MSG_WARN([bash not found, tests will be skipped])
|
||||
RUN_TESTS="no"],
|
||||
[RUN_TESTS="yes"
|
||||
AC_SUBST([BASH_PATH])])
|
||||
AC_SUBST([RUN_TESTS])
|
||||
|
||||
if test x$RUN_TESTS = xyes; then
|
||||
# List test files
|
||||
cur_dir=`pwd`
|
||||
cd "$srcdir"/tests
|
||||
|
||||
for sample_path in samples/*.mobi
|
||||
do
|
||||
TESTLIST="${TESTLIST} ${sample_path} \\
|
||||
"
|
||||
done
|
||||
for sample_path in samples/*.fail
|
||||
do
|
||||
TESTLIST="${TESTLIST} ${sample_path} \\
|
||||
"
|
||||
FAILLIST="${FAILLIST} ${sample_path} \\
|
||||
"
|
||||
done
|
||||
cd $cur_dir
|
||||
fi
|
||||
|
||||
AC_SUBST([TESTLIST])
|
||||
AC_SUBST([FAILLIST])
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_CONFIG_FILES([libmobi.pc])
|
||||
AC_CONFIG_FILES([src/Makefile])
|
||||
AC_CONFIG_FILES([tools/Makefile])
|
||||
AC_CONFIG_FILES([tools/mobitool.1])
|
||||
AC_CONFIG_FILES([tools/mobimeta.1])
|
||||
AC_CONFIG_FILES([tools/mobidrm.1])
|
||||
AC_CONFIG_FILES([tests/Makefile])
|
||||
AC_CONFIG_FILES([tests/test.sh], [chmod +x tests/test.sh])
|
||||
|
||||
AC_OUTPUT
|
||||
13
app/src/main/cpp/libmobi/libmobi.pc.in
vendored
Normal file
13
app/src/main/cpp/libmobi/libmobi.pc.in
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: libmobi
|
||||
Description: MOBI ebook format handling library
|
||||
URL: http://www.fabiszewski.net/libmobi
|
||||
Version: @VERSION@
|
||||
Requires:
|
||||
Libs: -L${libdir} -lmobi
|
||||
Libs.private: @LIBZ_LDFLAGS@ @LIBXML2_LDFLAGS@
|
||||
Cflags: -I${includedir}
|
||||
879
app/src/main/cpp/libmobi/mobi.xcodeproj/project.pbxproj
vendored
Normal file
879
app/src/main/cpp/libmobi/mobi.xcodeproj/project.pbxproj
vendored
Normal file
|
|
@ -0,0 +1,879 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1502448F1CD3A18F0075F4EC /* sha1.c in Sources */ = {isa = PBXBuildFile; fileRef = 1502448D1CD3A18F0075F4EC /* sha1.c */; };
|
||||
150244901CD3A18F0075F4EC /* sha1.h in Headers */ = {isa = PBXBuildFile; fileRef = 1502448E1CD3A18F0075F4EC /* sha1.h */; };
|
||||
1504FD851CBE880B002AA042 /* meta.c in Sources */ = {isa = PBXBuildFile; fileRef = 1504FD831CBE880B002AA042 /* meta.c */; };
|
||||
1504FD861CBE880B002AA042 /* meta.h in Headers */ = {isa = PBXBuildFile; fileRef = 1504FD841CBE880B002AA042 /* meta.h */; };
|
||||
150A318D18E19BF9001A7AD7 /* write.c in Sources */ = {isa = PBXBuildFile; fileRef = 150A318C18E19BF9001A7AD7 /* write.c */; };
|
||||
151A46661909312900FAF3F4 /* miniz.c in Sources */ = {isa = PBXBuildFile; fileRef = 151A46651909312900FAF3F4 /* miniz.c */; settings = {COMPILER_FLAGS = "-w"; }; };
|
||||
152FD1E6270509A900AF276A /* randombytes.h in Headers */ = {isa = PBXBuildFile; fileRef = 152FD1E4270509A900AF276A /* randombytes.h */; };
|
||||
152FD1E7270509A900AF276A /* randombytes.c in Sources */ = {isa = PBXBuildFile; fileRef = 152FD1E5270509A900AF276A /* randombytes.c */; };
|
||||
153D91DB18E9630000E807B6 /* memory.c in Sources */ = {isa = PBXBuildFile; fileRef = 153D91DA18E9630000E807B6 /* memory.c */; };
|
||||
1543065B1CB78A45006AB398 /* mobimeta.c in Sources */ = {isa = PBXBuildFile; fileRef = 151185A31CB6C28500201C8A /* mobimeta.c */; };
|
||||
1543065E1CB78BA8006AB398 /* libmobi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 150039BB18E06BC100D33077 /* libmobi.dylib */; };
|
||||
154C2D401CC64A170041DD0E /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 154C2D3E1CC64A170041DD0E /* common.c */; };
|
||||
154C2D411CC64A170041DD0E /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 154C2D3E1CC64A170041DD0E /* common.c */; };
|
||||
1550ADC318E427D7006F9257 /* buffer.c in Sources */ = {isa = PBXBuildFile; fileRef = 1550ADC218E427D7006F9257 /* buffer.c */; };
|
||||
1550ADCE18E4B925006F9257 /* compression.c in Sources */ = {isa = PBXBuildFile; fileRef = 1550ADCD18E4B925006F9257 /* compression.c */; };
|
||||
1553330118E359AE00334E23 /* read.c in Sources */ = {isa = PBXBuildFile; fileRef = 1553330018E359AE00334E23 /* read.c */; };
|
||||
1553332118E37FC400334E23 /* libmobi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 150039BB18E06BC100D33077 /* libmobi.dylib */; };
|
||||
15603889192D2E1A002EDB1A /* opf.c in Sources */ = {isa = PBXBuildFile; fileRef = 15603888192D2E1A002EDB1A /* opf.c */; };
|
||||
15615F0818F58C85004EBB6E /* mobitool.c in Sources */ = {isa = PBXBuildFile; fileRef = 15615F0718F58C85004EBB6E /* mobitool.c */; };
|
||||
1563314718EC36A200D4B858 /* debug.c in Sources */ = {isa = PBXBuildFile; fileRef = 1563314618EC36A200D4B858 /* debug.c */; };
|
||||
156AA65D1C81A3860085335A /* xmlwriter.c in Sources */ = {isa = PBXBuildFile; fileRef = 156AA65B1C81A3860085335A /* xmlwriter.c */; };
|
||||
156AA65E1C81A3860085335A /* xmlwriter.h in Headers */ = {isa = PBXBuildFile; fileRef = 156AA65C1C81A3860085335A /* xmlwriter.h */; };
|
||||
157BEA732747BEDA004984B8 /* libmobi.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 150039BB18E06BC100D33077 /* libmobi.dylib */; };
|
||||
157BEA852747BF13004984B8 /* mobidrm.c in Sources */ = {isa = PBXBuildFile; fileRef = 157BEA6B2747B4EC004984B8 /* mobidrm.c */; };
|
||||
157BEA8A2747BF26004984B8 /* common.c in Sources */ = {isa = PBXBuildFile; fileRef = 154C2D3E1CC64A170041DD0E /* common.c */; };
|
||||
157DF7AD191A514D00191502 /* index.c in Sources */ = {isa = PBXBuildFile; fileRef = 157DF7AC191A514D00191502 /* index.c */; };
|
||||
15AB2CB419572C2800EB7F74 /* parse_rawml.c in Sources */ = {isa = PBXBuildFile; fileRef = 15AB2CB319572C2800EB7F74 /* parse_rawml.c */; };
|
||||
15EA81DF1A14D5AC00138554 /* structure.c in Sources */ = {isa = PBXBuildFile; fileRef = 15EA81DE1A14D5AC00138554 /* structure.c */; };
|
||||
15F1A1D118F4192D009CFE05 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = 15F1A1D018F4192D009CFE05 /* util.c */; };
|
||||
15FB2BB21A1A32970052D5C5 /* encryption.c in Sources */ = {isa = PBXBuildFile; fileRef = 15FB2BB01A1A32970052D5C5 /* encryption.c */; };
|
||||
15FB2BB31A1A32970052D5C5 /* encryption.h in Headers */ = {isa = PBXBuildFile; fileRef = 15FB2BB11A1A32970052D5C5 /* encryption.h */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
1543065C1CB78B78006AB398 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 150039B318E06BC100D33077 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 150039BA18E06BC100D33077;
|
||||
remoteInfo = mobi;
|
||||
};
|
||||
1553331F18E37FB800334E23 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 150039B318E06BC100D33077 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 150039BA18E06BC100D33077;
|
||||
remoteInfo = mobi;
|
||||
};
|
||||
157BEA6E2747BEDA004984B8 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 150039B318E06BC100D33077 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 150039BA18E06BC100D33077;
|
||||
remoteInfo = mobi;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
154306521CB78A3D006AB398 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1/;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
1553331418E37F7000334E23 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
157BEA742747BEDA004984B8 /* CopyFiles */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = /usr/share/man/man1;
|
||||
dstSubfolderSpec = 0;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 1;
|
||||
};
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
150039BB18E06BC100D33077 /* libmobi.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libmobi.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
150039C218E06C1B00D33077 /* mobi.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = mobi.h; path = src/mobi.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
1502448D1CD3A18F0075F4EC /* sha1.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = sha1.c; path = src/sha1.c; sourceTree = "<group>"; };
|
||||
1502448E1CD3A18F0075F4EC /* sha1.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = sha1.h; path = src/sha1.h; sourceTree = "<group>"; };
|
||||
1504FD831CBE880B002AA042 /* meta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = meta.c; path = src/meta.c; sourceTree = "<group>"; };
|
||||
1504FD841CBE880B002AA042 /* meta.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = meta.h; path = src/meta.h; sourceTree = "<group>"; };
|
||||
150A318B18E19BD8001A7AD7 /* write.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = write.h; path = src/write.h; sourceTree = "<group>"; };
|
||||
150A318C18E19BF9001A7AD7 /* write.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = write.c; path = src/write.c; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
151185A31CB6C28500201C8A /* mobimeta.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = mobimeta.c; path = tools/mobimeta.c; sourceTree = SOURCE_ROOT; };
|
||||
151A46641909302C00FAF3F4 /* miniz.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = miniz.h; path = src/miniz.h; sourceTree = "<group>"; };
|
||||
151A46651909312900FAF3F4 /* miniz.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = miniz.c; path = src/miniz.c; sourceTree = "<absolute>"; };
|
||||
152D509E1BD79AE400E91C09 /* test.sh.in */ = {isa = PBXFileReference; explicitFileType = text.script.sh; name = test.sh.in; path = tests/test.sh.in; sourceTree = "<group>"; };
|
||||
152D50A01BD7A08300E91C09 /* Makefile.am */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.am; path = tests/Makefile.am; sourceTree = "<group>"; usesTabs = 1; xcLanguageSpecificationIdentifier = xcode.lang.sh; };
|
||||
152E5D1218F5DEB100B05EC9 /* configure.ac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = configure.ac; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.csh; };
|
||||
152E5D1318F5DEB100B05EC9 /* Makefile.am */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; path = Makefile.am; sourceTree = "<group>"; usesTabs = 1; };
|
||||
152E5D1418F5DEC000B05EC9 /* Makefile.am */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.am; path = src/Makefile.am; sourceTree = "<group>"; usesTabs = 1; };
|
||||
152E5D1518F5DECF00B05EC9 /* Makefile.am */ = {isa = PBXFileReference; explicitFileType = sourcecode.make; fileEncoding = 4; name = Makefile.am; path = tools/Makefile.am; sourceTree = "<group>"; usesTabs = 1; };
|
||||
152E5D1618F5E22000B05EC9 /* autogen.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = autogen.sh; sourceTree = "<group>"; };
|
||||
152ED797195EFBD900ACD1AD /* ChangeLog */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = ChangeLog; sourceTree = "<group>"; };
|
||||
152FD1E4270509A900AF276A /* randombytes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = randombytes.h; path = src/randombytes.h; sourceTree = "<group>"; };
|
||||
152FD1E5270509A900AF276A /* randombytes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = randombytes.c; path = src/randombytes.c; sourceTree = "<group>"; };
|
||||
153967601907C0AA00EDC923 /* COPYING */ = {isa = PBXFileReference; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
|
||||
153D91DA18E9630000E807B6 /* memory.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = memory.c; path = src/memory.c; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
153D91DC18E9633500E807B6 /* memory.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = memory.h; path = src/memory.h; sourceTree = "<group>"; };
|
||||
1542B8041C7FA5E800C5122F /* getopt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = getopt.c; path = tools/win32/getopt.c; sourceTree = SOURCE_ROOT; };
|
||||
1542B8051C7FA5E900C5122F /* getopt.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = getopt.h; path = tools/win32/getopt.h; sourceTree = SOURCE_ROOT; };
|
||||
154306541CB78A3D006AB398 /* mobimeta */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mobimeta; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
154C2D3E1CC64A170041DD0E /* common.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = common.c; path = tools/common.c; sourceTree = SOURCE_ROOT; };
|
||||
154C2D3F1CC64A170041DD0E /* common.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = common.h; path = tools/common.h; sourceTree = SOURCE_ROOT; };
|
||||
1550ADC218E427D7006F9257 /* buffer.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = buffer.c; path = src/buffer.c; sourceTree = "<group>"; };
|
||||
1550ADC418E42842006F9257 /* buffer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = buffer.h; path = src/buffer.h; sourceTree = "<group>"; };
|
||||
1550ADCD18E4B925006F9257 /* compression.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = compression.c; path = src/compression.c; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
1550ADCF18E4BB83006F9257 /* compression.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = compression.h; path = src/compression.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
|
||||
1553330018E359AE00334E23 /* read.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = read.c; path = src/read.c; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
1553330218E359B900334E23 /* read.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = read.h; path = src/read.h; sourceTree = "<group>"; };
|
||||
1553331618E37F7000334E23 /* mobitool */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mobitool; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1559D790191BB06700636661 /* config.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = config.h; path = src/config.h; sourceTree = "<group>"; };
|
||||
15603888192D2E1A002EDB1A /* opf.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = opf.c; path = src/opf.c; sourceTree = "<group>"; };
|
||||
1560388A192D2E34002EDB1A /* opf.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = opf.h; path = src/opf.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
15615F0718F58C85004EBB6E /* mobitool.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = mobitool.c; path = tools/mobitool.c; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
1563314518EC367300D4B858 /* debug.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = debug.h; path = src/debug.h; sourceTree = "<group>"; };
|
||||
1563314618EC36A200D4B858 /* debug.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = debug.c; path = src/debug.c; sourceTree = "<group>"; };
|
||||
156AA65B1C81A3860085335A /* xmlwriter.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = xmlwriter.c; path = src/xmlwriter.c; sourceTree = "<group>"; };
|
||||
156AA65C1C81A3860085335A /* xmlwriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = xmlwriter.h; path = src/xmlwriter.h; sourceTree = "<group>"; };
|
||||
157BEA6B2747B4EC004984B8 /* mobidrm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = mobidrm.c; path = tools/mobidrm.c; sourceTree = SOURCE_ROOT; };
|
||||
157BEA782747BEDA004984B8 /* mobidrm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mobidrm; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
157DF7AC191A514D00191502 /* index.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = index.c; path = src/index.c; sourceTree = "<group>"; };
|
||||
157DF7AE191A51A400191502 /* index.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = index.h; path = src/index.h; sourceTree = "<group>"; };
|
||||
15843DFE19215D0400587C89 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = text; path = README.md; sourceTree = "<group>"; };
|
||||
158F44DC191E88010000F44A /* libmobi.pc.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = libmobi.pc.in; sourceTree = "<group>"; };
|
||||
15AB2CB319572C2800EB7F74 /* parse_rawml.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = parse_rawml.c; path = src/parse_rawml.c; sourceTree = "<group>"; };
|
||||
15AB2CB519572C4400EB7F74 /* parse_rawml.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = parse_rawml.h; path = src/parse_rawml.h; sourceTree = "<group>"; };
|
||||
15B4311D2767840300B7E6A7 /* mobidrm.1.in */ = {isa = PBXFileReference; explicitFileType = text.man; name = mobidrm.1.in; path = tools/mobidrm.1.in; sourceTree = SOURCE_ROOT; };
|
||||
15D7CFD71A167A3A00F08927 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = tools/README.md; sourceTree = "<group>"; };
|
||||
15E5FAD91CC58B4D00F700D1 /* mobimeta.1.in */ = {isa = PBXFileReference; explicitFileType = text.man; name = mobimeta.1.in; path = tools/mobimeta.1.in; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.man; };
|
||||
15E65B5E1A1DF1DA00B7FBBD /* mobitool.1.in */ = {isa = PBXFileReference; explicitFileType = text.man; fileEncoding = 4; name = mobitool.1.in; path = tools/mobitool.1.in; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.man; };
|
||||
15E65B5F1A1E0FC100B7FBBD /* .travis.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .travis.yml; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.simpleColoring; };
|
||||
15EA81DD1A14D58500138554 /* structure.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = structure.h; path = src/structure.h; sourceTree = SOURCE_ROOT; };
|
||||
15EA81DE1A14D5AC00138554 /* structure.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = structure.c; path = src/structure.c; sourceTree = "<group>"; };
|
||||
15F1A1D018F4192D009CFE05 /* util.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; lineEnding = 0; name = util.c; path = src/util.c; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.c; };
|
||||
15F1A1D218F4195A009CFE05 /* util.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = util.h; path = src/util.h; sourceTree = "<group>"; };
|
||||
15FB2BB01A1A32970052D5C5 /* encryption.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = encryption.c; path = src/encryption.c; sourceTree = "<group>"; };
|
||||
15FB2BB11A1A32970052D5C5 /* encryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = encryption.h; path = src/encryption.h; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
150039B818E06BC100D33077 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
154306511CB78A3D006AB398 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1543065E1CB78BA8006AB398 /* libmobi.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1553331318E37F7000334E23 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1553332118E37FC400334E23 /* libmobi.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
157BEA722747BEDA004984B8 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
157BEA732747BEDA004984B8 /* libmobi.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
150039B218E06BC100D33077 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
152D509F1BD79AED00E91C09 /* tests */,
|
||||
1539675F1907BC0600EDC923 /* docs */,
|
||||
1550ADC218E427D7006F9257 /* buffer.c */,
|
||||
1550ADC418E42842006F9257 /* buffer.h */,
|
||||
1550ADCD18E4B925006F9257 /* compression.c */,
|
||||
1550ADCF18E4BB83006F9257 /* compression.h */,
|
||||
1559D790191BB06700636661 /* config.h */,
|
||||
1563314618EC36A200D4B858 /* debug.c */,
|
||||
1563314518EC367300D4B858 /* debug.h */,
|
||||
15FB2BB01A1A32970052D5C5 /* encryption.c */,
|
||||
15FB2BB11A1A32970052D5C5 /* encryption.h */,
|
||||
157DF7AC191A514D00191502 /* index.c */,
|
||||
157DF7AE191A51A400191502 /* index.h */,
|
||||
153D91DA18E9630000E807B6 /* memory.c */,
|
||||
153D91DC18E9633500E807B6 /* memory.h */,
|
||||
1504FD831CBE880B002AA042 /* meta.c */,
|
||||
1504FD841CBE880B002AA042 /* meta.h */,
|
||||
151A46651909312900FAF3F4 /* miniz.c */,
|
||||
151A46641909302C00FAF3F4 /* miniz.h */,
|
||||
150039C218E06C1B00D33077 /* mobi.h */,
|
||||
15603888192D2E1A002EDB1A /* opf.c */,
|
||||
1560388A192D2E34002EDB1A /* opf.h */,
|
||||
15AB2CB319572C2800EB7F74 /* parse_rawml.c */,
|
||||
15AB2CB519572C4400EB7F74 /* parse_rawml.h */,
|
||||
152FD1E4270509A900AF276A /* randombytes.h */,
|
||||
152FD1E5270509A900AF276A /* randombytes.c */,
|
||||
1553330018E359AE00334E23 /* read.c */,
|
||||
1553330218E359B900334E23 /* read.h */,
|
||||
1502448D1CD3A18F0075F4EC /* sha1.c */,
|
||||
1502448E1CD3A18F0075F4EC /* sha1.h */,
|
||||
15EA81DE1A14D5AC00138554 /* structure.c */,
|
||||
15EA81DD1A14D58500138554 /* structure.h */,
|
||||
15F1A1D018F4192D009CFE05 /* util.c */,
|
||||
15F1A1D218F4195A009CFE05 /* util.h */,
|
||||
150A318C18E19BF9001A7AD7 /* write.c */,
|
||||
150A318B18E19BD8001A7AD7 /* write.h */,
|
||||
156AA65B1C81A3860085335A /* xmlwriter.c */,
|
||||
156AA65C1C81A3860085335A /* xmlwriter.h */,
|
||||
1553331718E37F7100334E23 /* tools */,
|
||||
150039BC18E06BC100D33077 /* Products */,
|
||||
152E5CFE18F5DB3200B05EC9 /* autotools */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
150039BC18E06BC100D33077 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
150039BB18E06BC100D33077 /* libmobi.dylib */,
|
||||
1553331618E37F7000334E23 /* mobitool */,
|
||||
154306541CB78A3D006AB398 /* mobimeta */,
|
||||
157BEA782747BEDA004984B8 /* mobidrm */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
152D509F1BD79AED00E91C09 /* tests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
152D509E1BD79AE400E91C09 /* test.sh.in */,
|
||||
152D50A01BD7A08300E91C09 /* Makefile.am */,
|
||||
);
|
||||
name = tests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
152E5CFE18F5DB3200B05EC9 /* autotools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
152E5D1618F5E22000B05EC9 /* autogen.sh */,
|
||||
152E5D1218F5DEB100B05EC9 /* configure.ac */,
|
||||
158F44DC191E88010000F44A /* libmobi.pc.in */,
|
||||
152E5D1518F5DECF00B05EC9 /* Makefile.am */,
|
||||
152E5D1418F5DEC000B05EC9 /* Makefile.am */,
|
||||
152E5D1318F5DEB100B05EC9 /* Makefile.am */,
|
||||
);
|
||||
name = autotools;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1539675F1907BC0600EDC923 /* docs */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
152ED797195EFBD900ACD1AD /* ChangeLog */,
|
||||
15843DFE19215D0400587C89 /* README.md */,
|
||||
15D7CFD71A167A3A00F08927 /* README.md */,
|
||||
15E65B5F1A1E0FC100B7FBBD /* .travis.yml */,
|
||||
153967601907C0AA00EDC923 /* COPYING */,
|
||||
15B4311D2767840300B7E6A7 /* mobidrm.1.in */,
|
||||
15E65B5E1A1DF1DA00B7FBBD /* mobitool.1.in */,
|
||||
15E5FAD91CC58B4D00F700D1 /* mobimeta.1.in */,
|
||||
);
|
||||
name = docs;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
1553331718E37F7100334E23 /* tools */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
151185A31CB6C28500201C8A /* mobimeta.c */,
|
||||
15615F0718F58C85004EBB6E /* mobitool.c */,
|
||||
1542B8041C7FA5E800C5122F /* getopt.c */,
|
||||
1542B8051C7FA5E900C5122F /* getopt.h */,
|
||||
154C2D3E1CC64A170041DD0E /* common.c */,
|
||||
154C2D3F1CC64A170041DD0E /* common.h */,
|
||||
157BEA6B2747B4EC004984B8 /* mobidrm.c */,
|
||||
);
|
||||
name = tools;
|
||||
path = test;
|
||||
sourceTree = SOURCE_ROOT;
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXHeadersBuildPhase section */
|
||||
150039B918E06BC100D33077 /* Headers */ = {
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
15FB2BB31A1A32970052D5C5 /* encryption.h in Headers */,
|
||||
152FD1E6270509A900AF276A /* randombytes.h in Headers */,
|
||||
150244901CD3A18F0075F4EC /* sha1.h in Headers */,
|
||||
156AA65E1C81A3860085335A /* xmlwriter.h in Headers */,
|
||||
1504FD861CBE880B002AA042 /* meta.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXHeadersBuildPhase section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
150039BA18E06BC100D33077 /* mobi */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 150039BF18E06BC100D33077 /* Build configuration list for PBXNativeTarget "mobi" */;
|
||||
buildPhases = (
|
||||
150039B718E06BC100D33077 /* Sources */,
|
||||
150039B818E06BC100D33077 /* Frameworks */,
|
||||
150039B918E06BC100D33077 /* Headers */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = mobi;
|
||||
productName = libmobi;
|
||||
productReference = 150039BB18E06BC100D33077 /* libmobi.dylib */;
|
||||
productType = "com.apple.product-type.library.dynamic";
|
||||
};
|
||||
154306531CB78A3D006AB398 /* mobimeta */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1543065A1CB78A3D006AB398 /* Build configuration list for PBXNativeTarget "mobimeta" */;
|
||||
buildPhases = (
|
||||
154306501CB78A3D006AB398 /* Sources */,
|
||||
154306511CB78A3D006AB398 /* Frameworks */,
|
||||
154306521CB78A3D006AB398 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
1543065D1CB78B78006AB398 /* PBXTargetDependency */,
|
||||
);
|
||||
name = mobimeta;
|
||||
productName = write_test;
|
||||
productReference = 154306541CB78A3D006AB398 /* mobimeta */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
1553331518E37F7000334E23 /* mobitool */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 1553331C18E37F7100334E23 /* Build configuration list for PBXNativeTarget "mobitool" */;
|
||||
buildPhases = (
|
||||
1553331218E37F7000334E23 /* Sources */,
|
||||
1553331318E37F7000334E23 /* Frameworks */,
|
||||
1553331418E37F7000334E23 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
1553332018E37FB800334E23 /* PBXTargetDependency */,
|
||||
);
|
||||
name = mobitool;
|
||||
productName = test;
|
||||
productReference = 1553331618E37F7000334E23 /* mobitool */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
157BEA6C2747BEDA004984B8 /* mobidrm */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 157BEA752747BEDA004984B8 /* Build configuration list for PBXNativeTarget "mobidrm" */;
|
||||
buildPhases = (
|
||||
157BEA6F2747BEDA004984B8 /* Sources */,
|
||||
157BEA722747BEDA004984B8 /* Frameworks */,
|
||||
157BEA742747BEDA004984B8 /* CopyFiles */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
157BEA6D2747BEDA004984B8 /* PBXTargetDependency */,
|
||||
);
|
||||
name = mobidrm;
|
||||
productName = test;
|
||||
productReference = 157BEA782747BEDA004984B8 /* mobidrm */;
|
||||
productType = "com.apple.product-type.tool";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
150039B318E06BC100D33077 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1420;
|
||||
ORGANIZATIONNAME = "Bartek Fabiszewski";
|
||||
TargetAttributes = {
|
||||
154306531CB78A3D006AB398 = {
|
||||
CreatedOnToolsVersion = 7.3;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 150039B618E06BC100D33077 /* Build configuration list for PBXProject "mobi" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 150039B218E06BC100D33077;
|
||||
productRefGroup = 150039BC18E06BC100D33077 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
150039BA18E06BC100D33077 /* mobi */,
|
||||
1553331518E37F7000334E23 /* mobitool */,
|
||||
154306531CB78A3D006AB398 /* mobimeta */,
|
||||
157BEA6C2747BEDA004984B8 /* mobidrm */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
150039B718E06BC100D33077 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1550ADCE18E4B925006F9257 /* compression.c in Sources */,
|
||||
15EA81DF1A14D5AC00138554 /* structure.c in Sources */,
|
||||
1563314718EC36A200D4B858 /* debug.c in Sources */,
|
||||
15F1A1D118F4192D009CFE05 /* util.c in Sources */,
|
||||
1502448F1CD3A18F0075F4EC /* sha1.c in Sources */,
|
||||
15603889192D2E1A002EDB1A /* opf.c in Sources */,
|
||||
150A318D18E19BF9001A7AD7 /* write.c in Sources */,
|
||||
1550ADC318E427D7006F9257 /* buffer.c in Sources */,
|
||||
1553330118E359AE00334E23 /* read.c in Sources */,
|
||||
157DF7AD191A514D00191502 /* index.c in Sources */,
|
||||
153D91DB18E9630000E807B6 /* memory.c in Sources */,
|
||||
156AA65D1C81A3860085335A /* xmlwriter.c in Sources */,
|
||||
15AB2CB419572C2800EB7F74 /* parse_rawml.c in Sources */,
|
||||
152FD1E7270509A900AF276A /* randombytes.c in Sources */,
|
||||
15FB2BB21A1A32970052D5C5 /* encryption.c in Sources */,
|
||||
1504FD851CBE880B002AA042 /* meta.c in Sources */,
|
||||
151A46661909312900FAF3F4 /* miniz.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
154306501CB78A3D006AB398 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
154C2D411CC64A170041DD0E /* common.c in Sources */,
|
||||
1543065B1CB78A45006AB398 /* mobimeta.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
1553331218E37F7000334E23 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
154C2D401CC64A170041DD0E /* common.c in Sources */,
|
||||
15615F0818F58C85004EBB6E /* mobitool.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
157BEA6F2747BEDA004984B8 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
157BEA8A2747BF26004984B8 /* common.c in Sources */,
|
||||
157BEA852747BF13004984B8 /* mobidrm.c in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
1543065D1CB78B78006AB398 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 150039BA18E06BC100D33077 /* mobi */;
|
||||
targetProxy = 1543065C1CB78B78006AB398 /* PBXContainerItemProxy */;
|
||||
};
|
||||
1553332018E37FB800334E23 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 150039BA18E06BC100D33077 /* mobi */;
|
||||
targetProxy = 1553331F18E37FB800334E23 /* PBXContainerItemProxy */;
|
||||
};
|
||||
157BEA6D2747BEDA004984B8 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 150039BA18E06BC100D33077 /* mobi */;
|
||||
targetProxy = 157BEA6E2747BEDA004984B8 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
150039BD18E06BC100D33077 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_GCD_PERFORMANCE = YES;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
|
||||
CLANG_CXX_LIBRARY = "compiler-default";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = NO;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = "compiler-default";
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_PEDANTIC = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
/usr/include/libxml2,
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
OTHER_CFLAGS = (
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-DHAVE_STRDUP",
|
||||
);
|
||||
OTHER_LDFLAGS = "-lz";
|
||||
SDKROOT = macosx;
|
||||
USER_HEADER_SEARCH_PATHS = "";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
150039BE18E06BC100D33077 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_GCD_PERFORMANCE = YES;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "compiler-default";
|
||||
CLANG_CXX_LIBRARY = "compiler-default";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_ASSIGN_ENUM = NO;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_COMPLETION_HANDLER_MISUSE = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_FRAMEWORK_INCLUDE_PRIVATE_FROM_PUBLIC = YES;
|
||||
CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_SEMICOLON_BEFORE_METHOD_BODY = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES_AGGRESSIVE;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = "compiler-default";
|
||||
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES;
|
||||
GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_TREAT_WARNINGS_AS_ERRORS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES;
|
||||
GCC_WARN_ABOUT_MISSING_NEWLINE = YES;
|
||||
GCC_WARN_ABOUT_MISSING_PROTOTYPES = NO;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES;
|
||||
GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES;
|
||||
GCC_WARN_PEDANTIC = YES;
|
||||
GCC_WARN_SHADOW = YES;
|
||||
GCC_WARN_SIGN_COMPARE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNKNOWN_PRAGMAS = YES;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_LABEL = YES;
|
||||
GCC_WARN_UNUSED_PARAMETER = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
/usr/include/libxml2,
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
ONLY_ACTIVE_ARCH = NO;
|
||||
OTHER_CFLAGS = (
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-DHAVE_STRDUP",
|
||||
);
|
||||
OTHER_LDFLAGS = "-lz";
|
||||
SDKROOT = macosx;
|
||||
USER_HEADER_SEARCH_PATHS = "";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
150039C018E06BC100D33077 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2,
|
||||
);
|
||||
OTHER_CFLAGS = (
|
||||
"-DHAVE_CONFIG_H",
|
||||
"-DHAVE_STRDUP",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-lxml2",
|
||||
"-lz",
|
||||
);
|
||||
PRODUCT_NAME = mobi;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
150039C118E06BC100D33077 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
EXECUTABLE_PREFIX = lib;
|
||||
GCC_OPTIMIZATION_LEVEL = 3;
|
||||
GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
|
||||
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/usr/include/libxml2,
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"-lxml2",
|
||||
"-lz",
|
||||
);
|
||||
PRODUCT_NAME = mobi;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
154306581CB78A3D006AB398 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
./src,
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
154306591CB78A3D006AB398 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
./src,
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
1553331D18E37F7100334E23 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = ./src;
|
||||
"HEADER_SEARCH_PATHS[arch=*]" = ./src;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = mobitool;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
1553331E18E37F7100334E23 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
HEADER_SEARCH_PATHS = ./src;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
PRODUCT_NAME = mobitool;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
157BEA762747BEDA004984B8 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
HEADER_SEARCH_PATHS = ./src;
|
||||
"HEADER_SEARCH_PATHS[arch=*]" = ./src;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
157BEA772747BEDA004984B8 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CLANG_ANALYZER_SECURITY_FLOATLOOPCOUNTER = YES;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = "";
|
||||
HEADER_SEARCH_PATHS = ./src;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
150039B618E06BC100D33077 /* Build configuration list for PBXProject "mobi" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
150039BD18E06BC100D33077 /* Debug */,
|
||||
150039BE18E06BC100D33077 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
150039BF18E06BC100D33077 /* Build configuration list for PBXNativeTarget "mobi" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
150039C018E06BC100D33077 /* Debug */,
|
||||
150039C118E06BC100D33077 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1543065A1CB78A3D006AB398 /* Build configuration list for PBXNativeTarget "mobimeta" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
154306581CB78A3D006AB398 /* Debug */,
|
||||
154306591CB78A3D006AB398 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
1553331C18E37F7100334E23 /* Build configuration list for PBXNativeTarget "mobitool" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
1553331D18E37F7100334E23 /* Debug */,
|
||||
1553331E18E37F7100334E23 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
157BEA752747BEDA004984B8 /* Build configuration list for PBXNativeTarget "mobidrm" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
157BEA762747BEDA004984B8 /* Debug */,
|
||||
157BEA772747BEDA004984B8 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 150039B318E06BC100D33077 /* Project object */;
|
||||
}
|
||||
97
app/src/main/cpp/libmobi/msvc/libmobi.sln
vendored
Normal file
97
app/src/main/cpp/libmobi/msvc/libmobi.sln
vendored
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.1.32328.378
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libmobi", "libmobi.vcxproj", "{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mobidrm", "mobidrm\mobidrm.vcxproj", "{288A84A9-2AFD-4995-A397-E4554C8087AE}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mobitool", "mobitool\mobitool.vcxproj", "{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mobimeta", "mobimeta\mobimeta.vcxproj", "{2790D05E-6891-48E5-8450-F8D030763AD6}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
NoDependenciesDebug|x64 = NoDependenciesDebug|x64
|
||||
NoDependenciesDebug|x86 = NoDependenciesDebug|x86
|
||||
NoDependenciesRelease|x64 = NoDependenciesRelease|x64
|
||||
NoDependenciesRelease|x86 = NoDependenciesRelease|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Debug|x64.Build.0 = Debug|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Debug|x86.Build.0 = Debug|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesDebug|x64.ActiveCfg = NoDependenciesDebug|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesDebug|x64.Build.0 = NoDependenciesDebug|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesDebug|x86.ActiveCfg = NoDependenciesDebug|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesDebug|x86.Build.0 = NoDependenciesDebug|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesRelease|x64.ActiveCfg = NoDependenciesRelease|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesRelease|x64.Build.0 = NoDependenciesRelease|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesRelease|x86.ActiveCfg = NoDependenciesRelease|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.NoDependenciesRelease|x86.Build.0 = NoDependenciesRelease|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Release|x64.ActiveCfg = Release|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Release|x64.Build.0 = Release|x64
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Release|x86.ActiveCfg = Release|Win32
|
||||
{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}.Release|x86.Build.0 = Release|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Debug|x64.Build.0 = Debug|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Debug|x86.Build.0 = Debug|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesDebug|x64.ActiveCfg = Debug|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesDebug|x64.Build.0 = Debug|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesDebug|x86.ActiveCfg = Debug|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesDebug|x86.Build.0 = Debug|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesRelease|x64.ActiveCfg = Release|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesRelease|x64.Build.0 = Release|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesRelease|x86.ActiveCfg = Release|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.NoDependenciesRelease|x86.Build.0 = Release|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Release|x64.ActiveCfg = Release|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Release|x64.Build.0 = Release|x64
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Release|x86.ActiveCfg = Release|Win32
|
||||
{288A84A9-2AFD-4995-A397-E4554C8087AE}.Release|x86.Build.0 = Release|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Debug|x64.Build.0 = Debug|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Debug|x86.Build.0 = Debug|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesDebug|x64.ActiveCfg = Debug|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesDebug|x64.Build.0 = Debug|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesDebug|x86.ActiveCfg = Debug|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesDebug|x86.Build.0 = Debug|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesRelease|x64.ActiveCfg = Release|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesRelease|x64.Build.0 = Release|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesRelease|x86.ActiveCfg = Release|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.NoDependenciesRelease|x86.Build.0 = Release|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Release|x64.ActiveCfg = Release|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Release|x64.Build.0 = Release|x64
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Release|x86.ActiveCfg = Release|Win32
|
||||
{D8E9C708-FBD0-400C-B5B6-F8555FDE8767}.Release|x86.Build.0 = Release|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Debug|x64.Build.0 = Debug|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Debug|x86.Build.0 = Debug|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesDebug|x64.ActiveCfg = Debug|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesDebug|x64.Build.0 = Debug|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesDebug|x86.ActiveCfg = Debug|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesDebug|x86.Build.0 = Debug|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesRelease|x64.ActiveCfg = Release|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesRelease|x64.Build.0 = Release|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesRelease|x86.ActiveCfg = Release|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.NoDependenciesRelease|x86.Build.0 = Release|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Release|x64.ActiveCfg = Release|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Release|x64.Build.0 = Release|x64
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Release|x86.ActiveCfg = Release|Win32
|
||||
{2790D05E-6891-48E5-8450-F8D030763AD6}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {673B11AC-E877-465E-89D0-97CAA6C1B389}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
395
app/src/main/cpp/libmobi/msvc/libmobi.vcxproj
vendored
Normal file
395
app/src/main/cpp/libmobi/msvc/libmobi.vcxproj
vendored
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoDependenciesDebug|Win32">
|
||||
<Configuration>NoDependenciesDebug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoDependenciesDebug|x64">
|
||||
<Configuration>NoDependenciesDebug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoDependenciesRelease|Win32">
|
||||
<Configuration>NoDependenciesRelease</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="NoDependenciesRelease|x64">
|
||||
<Configuration>NoDependenciesRelease</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>17.0</VCProjectVersion>
|
||||
<ProjectGuid>{A48F597C-ADBC-499E-B282-0F8A2B1A4B5F}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>StaticLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;MOBI_DEBUG=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;MOBI_DEBUG=1;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>Disabled</Optimization>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
</Link>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ProjectReference />
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;MOBI_DEBUG=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">
|
||||
<ProjectReference />
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;MOBI_DEBUG=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">
|
||||
<ProjectReference />
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
</ClCompile>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ProjectReference />
|
||||
<ClCompile>
|
||||
<PreprocessorDefinitions>HAVE_CONFIG_H;USE_XMLWRITER;USE_ENCRYPTION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<ProjectReference />
|
||||
<ProjectReference />
|
||||
<PreBuildEvent>
|
||||
<Command>for /f "tokens=4 delims=[]" %%a in ('type "$(SolutionDir)..\configure.ac" ^| find "AC_INIT"') do (
|
||||
set version=%%a
|
||||
)
|
||||
echo Compiling libmobi version %version%
|
||||
echo #define PACKAGE_VERSION "%version%" > "$(SolutionDir)..\config.h"</Command>
|
||||
</PreBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\buffer.c" />
|
||||
<ClCompile Include="..\src\compression.c" />
|
||||
<ClCompile Include="..\src\debug.c" />
|
||||
<ClCompile Include="..\src\encryption.c" />
|
||||
<ClCompile Include="..\src\index.c" />
|
||||
<ClCompile Include="..\src\memory.c" />
|
||||
<ClCompile Include="..\src\meta.c" />
|
||||
<ClCompile Include="..\src\miniz.c">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\opf.c" />
|
||||
<ClCompile Include="..\src\parse_rawml.c" />
|
||||
<ClCompile Include="..\src\randombytes.c" />
|
||||
<ClCompile Include="..\src\read.c" />
|
||||
<ClCompile Include="..\src\sha1.c" />
|
||||
<ClCompile Include="..\src\structure.c" />
|
||||
<ClCompile Include="..\src\util.c" />
|
||||
<ClCompile Include="..\src\write.c" />
|
||||
<ClCompile Include="..\src\xmlwriter.c">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\src\buffer.h" />
|
||||
<ClInclude Include="..\src\compression.h" />
|
||||
<ClInclude Include="..\src\config.h" />
|
||||
<ClInclude Include="..\src\debug.h" />
|
||||
<ClInclude Include="..\src\encryption.h" />
|
||||
<ClInclude Include="..\src\index.h" />
|
||||
<ClInclude Include="..\src\memory.h" />
|
||||
<ClInclude Include="..\src\meta.h" />
|
||||
<ClInclude Include="..\src\miniz.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\src\mobi.h" />
|
||||
<ClInclude Include="..\src\opf.h" />
|
||||
<ClInclude Include="..\src\parse_rawml.h" />
|
||||
<ClInclude Include="..\src\randombytes.h" />
|
||||
<ClInclude Include="..\src\read.h" />
|
||||
<ClInclude Include="..\src\sha1.h" />
|
||||
<ClInclude Include="..\src\structure.h" />
|
||||
<ClInclude Include="..\src\util.h" />
|
||||
<ClInclude Include="..\src\write.h" />
|
||||
<ClInclude Include="..\src\xmlwriter.h">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">false</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesDebug|x64'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='NoDependenciesRelease|x64'">true</ExcludedFromBuild>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets" Condition="Exists('packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" />
|
||||
<Import Project="packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets" Condition="Exists('packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets'))" />
|
||||
<Error Condition="!Exists('packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', 'packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
182
app/src/main/cpp/libmobi/msvc/mobidrm/mobidrm.vcxproj
vendored
Normal file
182
app/src/main/cpp/libmobi/msvc/mobidrm/mobidrm.vcxproj
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\tools\common.c" />
|
||||
<ClCompile Include="..\..\tools\mobidrm.c" />
|
||||
<ClCompile Include="..\..\tools\win32\getopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\tools\common.h" />
|
||||
<ClInclude Include="..\..\tools\win32\getopt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libmobi.vcxproj">
|
||||
<Project>{a48f597c-adbc-499e-b282-0f8a2b1a4b5f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{288a84a9-2afd-4995-a397-e4554c8087ae}</ProjectGuid>
|
||||
<RootNamespace>mobidrm</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets" Condition="Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" />
|
||||
<Import Project="..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets" Condition="Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
5
app/src/main/cpp/libmobi/msvc/mobidrm/packages.config
vendored
Normal file
5
app/src/main/cpp/libmobi/msvc/mobidrm/packages.config
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="libxml2-vc140-static-32_64" version="2.9.4.1" targetFramework="native" />
|
||||
<package id="zlib128-vc140-static-32_64" version="1.2.8" targetFramework="native" />
|
||||
</packages>
|
||||
182
app/src/main/cpp/libmobi/msvc/mobimeta/mobimeta.vcxproj
vendored
Normal file
182
app/src/main/cpp/libmobi/msvc/mobimeta/mobimeta.vcxproj
vendored
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\tools\common.c" />
|
||||
<ClCompile Include="..\..\tools\mobimeta.c" />
|
||||
<ClCompile Include="..\..\tools\win32\getopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\tools\common.h" />
|
||||
<ClInclude Include="..\..\tools\win32\getopt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libmobi.vcxproj">
|
||||
<Project>{a48f597c-adbc-499e-b282-0f8a2b1a4b5f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{2790d05e-6891-48e5-8450-f8d030763ad6}</ProjectGuid>
|
||||
<RootNamespace>mobimeta</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets" Condition="Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" />
|
||||
<Import Project="..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets" Condition="Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
5
app/src/main/cpp/libmobi/msvc/mobimeta/packages.config
vendored
Normal file
5
app/src/main/cpp/libmobi/msvc/mobimeta/packages.config
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="libxml2-vc140-static-32_64" version="2.9.4.1" targetFramework="native" />
|
||||
<package id="zlib128-vc140-static-32_64" version="1.2.8" targetFramework="native" />
|
||||
</packages>
|
||||
184
app/src/main/cpp/libmobi/msvc/mobitool/mobitool.vcxproj
vendored
Normal file
184
app/src/main/cpp/libmobi/msvc/mobitool/mobitool.vcxproj
vendored
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\src\miniz.c" />
|
||||
<ClCompile Include="..\..\tools\common.c" />
|
||||
<ClCompile Include="..\..\tools\mobitool.c" />
|
||||
<ClCompile Include="..\..\tools\win32\getopt.c" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\src\miniz.h" />
|
||||
<ClInclude Include="..\..\tools\common.h" />
|
||||
<ClInclude Include="..\..\tools\win32\getopt.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\libmobi.vcxproj">
|
||||
<Project>{a48f597c-adbc-499e-b282-0f8a2b1a4b5f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<ProjectGuid>{d8e9c708-fbd0-400c-b5b6-f8555fde8767}</ProjectGuid>
|
||||
<RootNamespace>mobitool</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Linkage-libxml2>static</Linkage-libxml2>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;MOBI_DEBUG=1WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;MOBI_DEBUG=1;MOBI_DEBUG=1_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmtd.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>USE_XMLWRITER;USE_ENCRYPTION;_CRT_SECURE_NO_WARNINGS;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>$(SolutionDir)\..\src</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalDependencies>ws2_32.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalOptions>/NODEFAULTLIB:libcmt.lib</AdditionalOptions>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets" Condition="Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" />
|
||||
<Import Project="..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets" Condition="Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" />
|
||||
</ImportGroup>
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\zlib128-vc140-static-32_64.1.2.8\build\native\zlib128-vc140-static-32_64.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\libxml2-vc140-static-32_64.2.9.4.1\build\native\libxml2-vc140-static-32_64.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
5
app/src/main/cpp/libmobi/msvc/mobitool/packages.config
vendored
Normal file
5
app/src/main/cpp/libmobi/msvc/mobitool/packages.config
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="libxml2-vc140-static-32_64" version="2.9.4.1" targetFramework="native" />
|
||||
<package id="zlib128-vc140-static-32_64" version="1.2.8" targetFramework="native" />
|
||||
</packages>
|
||||
5
app/src/main/cpp/libmobi/msvc/packages.config
vendored
Normal file
5
app/src/main/cpp/libmobi/msvc/packages.config
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="libxml2-vc140-static-32_64" version="2.9.4.1" targetFramework="native" />
|
||||
<package id="zlib128-vc140-static-32_64" version="1.2.8" targetFramework="native" />
|
||||
</packages>
|
||||
87
app/src/main/cpp/libmobi/src/CMakeLists.txt
vendored
Normal file
87
app/src/main/cpp/libmobi/src/CMakeLists.txt
vendored
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# Copyright (c) 2022 Bartek Fabiszewski
|
||||
# http://www.fabiszewski.net
|
||||
#
|
||||
# This file is part of libmobi.
|
||||
# Licensed under LGPL, either version 3, or any later.
|
||||
# See <http://www.gnu.org/licenses/>
|
||||
|
||||
set(mobi_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/buffer.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/buffer.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compression.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/compression.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/config.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/debug.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/debug.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/index.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/index.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/memory.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/memory.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/meta.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/meta.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mobi.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parse_rawml.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/parse_rawml.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/read.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/read.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/structure.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/structure.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/util.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/write.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/write.h
|
||||
)
|
||||
|
||||
if(USE_ENCRYPTION)
|
||||
list(APPEND mobi_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/encryption.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/encryption.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sha1.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sha1.h
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/randombytes.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/randombytes.h)
|
||||
endif(USE_ENCRYPTION)
|
||||
|
||||
if(USE_XMLWRITER)
|
||||
list(APPEND mobi_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/opf.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/opf.h)
|
||||
if(NOT USE_LIBXML2)
|
||||
list(APPEND mobi_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/xmlwriter.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xmlwriter.h)
|
||||
endif(NOT USE_LIBXML2)
|
||||
endif(USE_XMLWRITER)
|
||||
|
||||
|
||||
add_library(mobi ${mobi_SOURCES})
|
||||
|
||||
set_target_properties(mobi PROPERTIES
|
||||
OUTPUT_NAME "mobi"
|
||||
SOVERSION ${PACKAGE_VERSION_MAJOR}
|
||||
VERSION "${PACKAGE_VERSION}"
|
||||
POSITION_INDEPENDENT_CODE ${BUILD_SHARED_LIBS}
|
||||
C_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON
|
||||
MACOSX_RPATH 1)
|
||||
|
||||
if(USE_MINIZ)
|
||||
set(miniz_SOURCES
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/miniz.c
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/miniz.h
|
||||
)
|
||||
add_library(miniz OBJECT ${miniz_SOURCES})
|
||||
target_compile_definitions(miniz PRIVATE
|
||||
MINIZ_NO_STDIO
|
||||
MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||
MINIZ_NO_TIME
|
||||
MINIZ_NO_ARCHIVE_APIS
|
||||
MINIZ_NO_ARCHIVE_WRITING_APIS
|
||||
_POSIX_C_SOURCE=200112L)
|
||||
target_link_libraries(mobi PRIVATE miniz)
|
||||
endif(USE_MINIZ)
|
||||
|
||||
if(USE_LIBXML2)
|
||||
target_link_libraries(mobi PUBLIC LibXml2::LibXml2)
|
||||
endif(USE_LIBXML2)
|
||||
|
||||
if(USE_ZLIB)
|
||||
target_link_libraries(mobi PUBLIC ZLIB::ZLIB)
|
||||
endif(USE_ZLIB)
|
||||
27
app/src/main/cpp/libmobi/src/Makefile.am
vendored
Normal file
27
app/src/main/cpp/libmobi/src/Makefile.am
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# libmobi
|
||||
|
||||
lib_LTLIBRARIES = libmobi.la
|
||||
libmobi_la_SOURCES = buffer.c buffer.h compression.c compression.h config.h debug.c debug.h index.c index.h memory.c memory.h \
|
||||
meta.c meta.h parse_rawml.c parse_rawml.h read.c read.h structure.c structure.h util.c util.h write.c write.h
|
||||
|
||||
if USE_XMLWRITER
|
||||
libmobi_la_SOURCES += opf.c opf.h
|
||||
if !USE_LIBXML2
|
||||
libmobi_la_SOURCES += xmlwriter.c xmlwriter.h
|
||||
endif
|
||||
endif
|
||||
if USE_ENCRYPTION
|
||||
libmobi_la_SOURCES += encryption.c encryption.h randombytes.c randombytes.h sha1.c sha1.h
|
||||
endif
|
||||
EXTRA_LTLIBRARIES = libminiz.la
|
||||
libminiz_la_SOURCES = miniz.c miniz.h
|
||||
libminiz_la_CFLAGS = $(VISIBILITY_HIDDEN) $(MINIZ_CFLAGS) \
|
||||
-DMINIZ_NO_STDIO -DMINIZ_NO_ZLIB_COMPATIBLE_NAMES \
|
||||
-DMINIZ_NO_TIME -DMINIZ_NO_ARCHIVE_APIS -DMINIZ_NO_ARCHIVE_WRITING_APIS
|
||||
libminiz_la_LDFLAGS =
|
||||
if USE_MINIZ
|
||||
libmobi_la_LIBADD = libminiz.la
|
||||
endif
|
||||
include_HEADERS = mobi.h
|
||||
libmobi_la_LDFLAGS = $(AVOID_VERSION) $(NO_UNDEFINED) $(DARWIN_LDFLAGS) $(LIBZ_LDFLAGS) $(LIBXML2_LDFLAGS)
|
||||
libmobi_la_CFLAGS = $(VISIBILITY_HIDDEN) $(ISO99_SOURCE) $(DEBUG_CFLAGS) $(LIBXML2_CFLAGS)
|
||||
637
app/src/main/cpp/libmobi/src/buffer.c
vendored
Normal file
637
app/src/main/cpp/libmobi/src/buffer.c
vendored
Normal file
|
|
@ -0,0 +1,637 @@
|
|||
/** @file buffer.c
|
||||
* @brief Functions to read/write raw big endian data
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "buffer.h"
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
@brief Initializer for MOBIBuffer structure
|
||||
|
||||
It allocates memory for structure and for data.
|
||||
Memory should be freed with mobi_buffer_free().
|
||||
|
||||
@param[in] len Size of data to be allocated for the buffer
|
||||
@return MOBIBuffer on success, NULL otherwise
|
||||
*/
|
||||
MOBIBuffer * mobi_buffer_init(const size_t len) {
|
||||
unsigned char *data = malloc(len);
|
||||
if (data == NULL) {
|
||||
debug_print("%s", "Buffer data allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(data, len);
|
||||
if (buf == NULL) {
|
||||
free(data);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Initializer for MOBIBuffer structure
|
||||
|
||||
It allocates memory for structure but, unlike mobi_buffer_init(), it does not allocate memory for data.
|
||||
Instead it works on external data.
|
||||
Memory should be freed with mobi_buffer_free_null() (buf->data will not be deallocated).
|
||||
|
||||
@param[in,out] data Set data as buffer data
|
||||
@param[in] len Size of data held by the buffer
|
||||
@return MOBIBuffer on success, NULL otherwise
|
||||
*/
|
||||
MOBIBuffer * mobi_buffer_init_null(unsigned char *data, const size_t len) {
|
||||
MOBIBuffer *buf = malloc(sizeof(MOBIBuffer));
|
||||
if (buf == NULL) {
|
||||
debug_print("%s", "Buffer allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
buf->data = data;
|
||||
buf->offset = 0;
|
||||
buf->maxlen = len;
|
||||
buf->error = MOBI_SUCCESS;
|
||||
return buf;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Resize buffer
|
||||
|
||||
Smaller size than offset will cause data truncation.
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] newlen New buffer size
|
||||
*/
|
||||
void mobi_buffer_resize(MOBIBuffer *buf, const size_t newlen) {
|
||||
unsigned char *tmp = realloc(buf->data, newlen);
|
||||
if (tmp == NULL) {
|
||||
debug_print("%s", "Buffer allocation failed\n");
|
||||
buf->error = MOBI_MALLOC_FAILED;
|
||||
return;
|
||||
}
|
||||
buf->data = tmp;
|
||||
buf->maxlen = newlen;
|
||||
if (buf->offset >= newlen) {
|
||||
buf->offset = newlen - 1;
|
||||
}
|
||||
debug_print("Buffer successfully resized to %zu\n", newlen);
|
||||
buf->error = MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds 8-bit value to MOBIBuffer
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] data Integer to be put into the buffer
|
||||
*/
|
||||
void mobi_buffer_add8(MOBIBuffer *buf, const uint8_t data) {
|
||||
if (buf->offset + 1 > buf->maxlen) {
|
||||
debug_print("%s", "Buffer full\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
buf->data[buf->offset++] = data;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds 16-bit value to MOBIBuffer
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] data Integer to be put into the buffer
|
||||
*/
|
||||
void mobi_buffer_add16(MOBIBuffer *buf, const uint16_t data) {
|
||||
if (buf->offset + 2 > buf->maxlen) {
|
||||
debug_print("%s", "Buffer full\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
unsigned char *buftr = buf->data + buf->offset;
|
||||
*buftr++ = (uint8_t)((uint32_t)(data & 0xff00U) >> 8);
|
||||
*buftr = (uint8_t)((uint32_t)(data & 0xffU));
|
||||
buf->offset += 2;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds 32-bit value to MOBIBuffer
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] data Integer to be put into the buffer
|
||||
*/
|
||||
void mobi_buffer_add32(MOBIBuffer *buf, const uint32_t data) {
|
||||
if (buf->offset + 4 > buf->maxlen) {
|
||||
debug_print("%s", "Buffer full\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
unsigned char *buftr = buf->data + buf->offset;
|
||||
*buftr++ = (uint8_t)((uint32_t)(data & 0xff000000U) >> 24);
|
||||
*buftr++ = (uint8_t)((uint32_t)(data & 0xff0000U) >> 16);
|
||||
*buftr++ = (uint8_t)((uint32_t)(data & 0xff00U) >> 8);
|
||||
*buftr = (uint8_t)((uint32_t)(data & 0xffU));
|
||||
buf->offset += 4;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds raw data to MOBIBuffer
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] data Pointer to read data
|
||||
@param[in] len Size of the read data
|
||||
*/
|
||||
void mobi_buffer_addraw(MOBIBuffer *buf, const unsigned char* data, const size_t len) {
|
||||
if (buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "Buffer full\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
memcpy(buf->data + buf->offset, data, len);
|
||||
buf->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds string to MOBIBuffer without null terminator
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] str Pointer to string
|
||||
*/
|
||||
void mobi_buffer_addstring(MOBIBuffer *buf, const char *str) {
|
||||
const size_t len = strlen(str);
|
||||
mobi_buffer_addraw(buf, (const unsigned char *) str, len);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Adds count of zeroes to MOBIBuffer
|
||||
|
||||
@param[in,out] buf MOBIBuffer structure to be filled with data
|
||||
@param[in] count Number of zeroes to be put into the buffer
|
||||
*/
|
||||
void mobi_buffer_addzeros(MOBIBuffer *buf, const size_t count) {
|
||||
if (buf->offset + count > buf->maxlen) {
|
||||
debug_print("%s", "Buffer full\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
memset(buf->data + buf->offset, 0, count);
|
||||
buf->offset += count;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads 8-bit value from MOBIBuffer
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
uint8_t mobi_buffer_get8(MOBIBuffer *buf) {
|
||||
if (buf->offset + 1 > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return 0;
|
||||
}
|
||||
return buf->data[buf->offset++];
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads 16-bit value from MOBIBuffer
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
uint16_t mobi_buffer_get16(MOBIBuffer *buf) {
|
||||
if (buf->offset + 2 > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return 0;
|
||||
}
|
||||
uint16_t val;
|
||||
val = (uint16_t)((uint16_t) buf->data[buf->offset] << 8 | (uint16_t) buf->data[buf->offset + 1]);
|
||||
buf->offset += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads 32-bit value from MOBIBuffer
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
uint32_t mobi_buffer_get32(MOBIBuffer *buf) {
|
||||
if (buf->offset + 4 > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return 0;
|
||||
}
|
||||
uint32_t val;
|
||||
val = (uint32_t) buf->data[buf->offset] << 24 | (uint32_t) buf->data[buf->offset + 1] << 16 | (uint32_t) buf->data[buf->offset + 2] << 8 | (uint32_t) buf->data[buf->offset + 3];
|
||||
buf->offset += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads variable length value from MOBIBuffer
|
||||
|
||||
Internal function for wrappers:
|
||||
mobi_buffer_get_varlen();
|
||||
mobi_buffer_get_varlen_dec();
|
||||
|
||||
Reads maximum 4 bytes from the buffer. Stops when byte has bit 7 set.
|
||||
|
||||
This function has a limitation while reading backwards.
|
||||
In such case it will not read first byte in a buffer, as it would cause buffer offset to underflow.
|
||||
That means that going bacwards it cannot read variable length values that are placed at the beginning of a buffer.
|
||||
This will result in an error.
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[out] len Value will be increased by number of bytes read
|
||||
@param[in] direction 1 - read buffer forward, -1 - read buffer backwards
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
static uint32_t mobi_buffer_get_varlen_internal(MOBIBuffer *buf, size_t *len, const int direction) {
|
||||
bool has_stop = false;
|
||||
uint32_t val = 0;
|
||||
uint8_t byte_count = 0;
|
||||
size_t max_count = direction == 1 ? buf->maxlen - buf->offset : buf->offset;
|
||||
if (buf->offset < buf->maxlen && max_count) {
|
||||
max_count = max_count < 4 ? max_count : 4;
|
||||
uint8_t byte;
|
||||
const uint8_t stop_flag = 0x80;
|
||||
const uint8_t mask = 0x7f;
|
||||
uint32_t shift = 0;
|
||||
unsigned char *p = buf->data + buf->offset;
|
||||
do {
|
||||
if (direction == 1) {
|
||||
byte = *p++;
|
||||
val <<= 7;
|
||||
val |= (byte & mask);
|
||||
} else {
|
||||
byte = *p--;
|
||||
val = val | (uint32_t)(byte & mask) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
byte_count++;
|
||||
has_stop = byte & stop_flag;
|
||||
} while (!has_stop && (byte_count < max_count));
|
||||
}
|
||||
if (!has_stop) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return 0;
|
||||
}
|
||||
*len += byte_count;
|
||||
buf->offset = direction == 1 ? buf->offset + byte_count : buf->offset - byte_count;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads variable length value from MOBIBuffer
|
||||
|
||||
Reads maximum 4 bytes from the buffer. Stops when byte has bit 7 set.
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[out] len Value will be increased by number of bytes read
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
uint32_t mobi_buffer_get_varlen(MOBIBuffer *buf, size_t *len) {
|
||||
return mobi_buffer_get_varlen_internal(buf, len, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads variable length value from MOBIBuffer going backwards
|
||||
|
||||
Reads maximum 4 bytes from the buffer. Stops when byte has bit 7 set.
|
||||
|
||||
This function has a limitation. It will not read first byte in a buffer, as it would cause buffer offset to underflow.
|
||||
That means that it cannot read variable length values that are placed at the beginning of a buffer.
|
||||
This will result in an error.
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[out] len Value will be increased by number of bytes read
|
||||
@return Read value, 0 if end of buffer is encountered
|
||||
*/
|
||||
uint32_t mobi_buffer_get_varlen_dec(MOBIBuffer *buf, size_t *len) {
|
||||
return mobi_buffer_get_varlen_internal(buf, len, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads raw data from MOBIBuffer and pads it with zero character
|
||||
|
||||
@param[out] str Destination for string read from buffer. Length must be (len + 1)
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[in] len Length of the data to be read from buffer
|
||||
*/
|
||||
void mobi_buffer_getstring(char *str, MOBIBuffer *buf, const size_t len) {
|
||||
if (!str) {
|
||||
buf->error = MOBI_PARAM_ERR;
|
||||
return;
|
||||
}
|
||||
if (buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
str[0] = '\0';
|
||||
return;
|
||||
}
|
||||
memcpy(str, buf->data + buf->offset, len);
|
||||
str[len] = '\0';
|
||||
buf->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads raw data from MOBIBuffer, appends it to a string and pads it with zero character
|
||||
|
||||
@param[in,out] str A string to which data will be appended
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[in] len Length of the data to be read from buffer
|
||||
*/
|
||||
void mobi_buffer_appendstring(char *str, MOBIBuffer *buf, const size_t len) {
|
||||
if (!str) {
|
||||
buf->error = MOBI_PARAM_ERR;
|
||||
return;
|
||||
}
|
||||
if (buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
size_t str_len = strlen(str);
|
||||
memcpy(str + str_len, buf->data + buf->offset, len);
|
||||
str[str_len + len] = '\0';
|
||||
buf->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Reads raw data from MOBIBuffer
|
||||
|
||||
@param[out] data Destination to which data will be appended
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[in] len Length of the data to be read from buffer
|
||||
*/
|
||||
void mobi_buffer_getraw(void *data, MOBIBuffer *buf, const size_t len) {
|
||||
if (!data) {
|
||||
buf->error = MOBI_PARAM_ERR;
|
||||
return;
|
||||
}
|
||||
if (buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
memcpy(data, buf->data + buf->offset, len);
|
||||
buf->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get pointer to MOBIBuffer data at offset
|
||||
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
@param[in] len Check if requested length is available in buffer
|
||||
@return Pointer to offset, or NULL on failure
|
||||
*/
|
||||
unsigned char * mobi_buffer_getpointer(MOBIBuffer *buf, const size_t len) {
|
||||
if (buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return NULL;
|
||||
}
|
||||
buf->offset += len;
|
||||
return buf->data + buf->offset - len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read 8-bit value from MOBIBuffer into allocated memory
|
||||
|
||||
Read 8-bit value from buffer into memory allocated by the function.
|
||||
Returns pointer to the value, which must be freed later.
|
||||
If the data is not accessible function will return null pointer.
|
||||
|
||||
@param[out] val Pointer to value or null pointer on failure
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
*/
|
||||
void mobi_buffer_dup8(uint8_t **val, MOBIBuffer *buf) {
|
||||
*val = NULL;
|
||||
if (buf->offset + 1 > buf->maxlen) {
|
||||
return;
|
||||
}
|
||||
*val = malloc(sizeof(uint8_t));
|
||||
if (*val == NULL) {
|
||||
return;
|
||||
}
|
||||
**val = mobi_buffer_get8(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read 16-bit value from MOBIBuffer into allocated memory
|
||||
|
||||
Read 16-bit value from buffer into allocated memory.
|
||||
Returns pointer to the value, which must be freed later.
|
||||
If the data is not accessible function will return null pointer.
|
||||
|
||||
@param[out] val Pointer to value or null pointer on failure
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
*/
|
||||
void mobi_buffer_dup16(uint16_t **val, MOBIBuffer *buf) {
|
||||
*val = NULL;
|
||||
if (buf->offset + 2 > buf->maxlen) {
|
||||
return;
|
||||
}
|
||||
*val = malloc(sizeof(uint16_t));
|
||||
if (*val == NULL) {
|
||||
return;
|
||||
}
|
||||
**val = mobi_buffer_get16(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read 32-bit value from MOBIBuffer into allocated memory
|
||||
|
||||
Read 32-bit value from buffer into allocated memory.
|
||||
Returns pointer to the value, which must be freed later.
|
||||
If the data is not accessible function will return null pointer.
|
||||
|
||||
@param[out] val Pointer to value
|
||||
@param[in] buf MOBIBuffer structure containing data
|
||||
*/
|
||||
void mobi_buffer_dup32(uint32_t **val, MOBIBuffer *buf) {
|
||||
*val = NULL;
|
||||
if (buf->offset + 4 > buf->maxlen) {
|
||||
return;
|
||||
}
|
||||
*val = malloc(sizeof(uint32_t));
|
||||
if (*val == NULL) {
|
||||
return;
|
||||
}
|
||||
**val = mobi_buffer_get32(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Copy 8-bit value from one MOBIBuffer into another
|
||||
|
||||
@param[out] dest Destination buffer
|
||||
@param[in] source Source buffer
|
||||
*/
|
||||
void mobi_buffer_copy8(MOBIBuffer *dest, MOBIBuffer *source) {
|
||||
mobi_buffer_add8(dest, mobi_buffer_get8(source));
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Copy raw value from one MOBIBuffer into another
|
||||
|
||||
@param[out] dest Destination buffer
|
||||
@param[in] source Source buffer
|
||||
@param[in] len Number of bytes to copy
|
||||
*/
|
||||
void mobi_buffer_copy(MOBIBuffer *dest, MOBIBuffer *source, const size_t len) {
|
||||
if (source->offset + len > source->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
source->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
if (dest->offset + len > dest->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
dest->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
memcpy(dest->data + dest->offset, source->data + source->offset, len);
|
||||
dest->offset += len;
|
||||
source->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Copy raw value within one MOBIBuffer
|
||||
|
||||
Memmove len bytes from offset (relative to current position)
|
||||
to current position in buffer and advance buffer position.
|
||||
Data may overlap.
|
||||
|
||||
@param[out] buf Buffer
|
||||
@param[in] offset Offset to read from
|
||||
@param[in] len Number of bytes to copy
|
||||
*/
|
||||
void mobi_buffer_move(MOBIBuffer *buf, const int offset, const size_t len) {
|
||||
size_t aoffset = (size_t) abs(offset);
|
||||
unsigned char *source = buf->data + buf->offset;
|
||||
if (offset >= 0) {
|
||||
if (buf->offset + aoffset + len > buf->maxlen) {
|
||||
debug_print("%s", "End of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
source += aoffset;
|
||||
} else {
|
||||
if ( (buf->offset < aoffset) || (buf->offset + len > buf->maxlen) ) {
|
||||
debug_print("%s", "Beyond start/end of buffer\n");
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
return;
|
||||
}
|
||||
source -= aoffset;
|
||||
}
|
||||
memmove(buf->data + buf->offset, source, len);
|
||||
buf->offset += len;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Check if buffer data header contains magic signature
|
||||
|
||||
@param[in] buf MOBIBuffer buffer containing data
|
||||
@param[in] magic Magic signature
|
||||
@return boolean true on match, false otherwise
|
||||
*/
|
||||
bool mobi_buffer_match_magic(MOBIBuffer *buf, const char *magic) {
|
||||
const size_t magic_length = strlen(magic);
|
||||
if (buf->offset + magic_length > buf->maxlen) {
|
||||
return false;
|
||||
}
|
||||
if (memcmp(buf->data + buf->offset, magic, magic_length) == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Check if buffer contains magic signature at given offset
|
||||
|
||||
@param[in] buf MOBIBuffer buffer containing data
|
||||
@param[in] magic Magic signature
|
||||
@param[in] offset Offset
|
||||
@return boolean true on match, false otherwise
|
||||
*/
|
||||
bool mobi_buffer_match_magic_offset(MOBIBuffer *buf, const char *magic, const size_t offset) {
|
||||
bool match = false;
|
||||
if (offset <= buf->maxlen) {
|
||||
const size_t save_offset = buf->offset;
|
||||
buf->offset = offset;
|
||||
match = mobi_buffer_match_magic(buf, magic);
|
||||
buf->offset = save_offset;
|
||||
}
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Move current buffer offset by diff bytes
|
||||
|
||||
@param[in,out] buf MOBIBuffer buffer containing data
|
||||
@param[in] diff Number of bytes by which the offset is adjusted
|
||||
*/
|
||||
void mobi_buffer_seek(MOBIBuffer *buf, const int diff) {
|
||||
size_t adiff = (size_t) abs(diff);
|
||||
if (diff >= 0) {
|
||||
if (buf->offset + adiff <= buf->maxlen) {
|
||||
buf->offset += adiff;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (buf->offset >= adiff) {
|
||||
buf->offset -= adiff;
|
||||
return;
|
||||
}
|
||||
}
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
debug_print("%s", "End of buffer\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set buffer offset to pos position
|
||||
|
||||
@param[in,out] buf MOBIBuffer buffer containing data
|
||||
@param[in] pos New position
|
||||
*/
|
||||
void mobi_buffer_setpos(MOBIBuffer *buf, const size_t pos) {
|
||||
if (pos <= buf->maxlen) {
|
||||
buf->offset = pos;
|
||||
return;
|
||||
}
|
||||
buf->error = MOBI_BUFFER_END;
|
||||
debug_print("%s", "End of buffer\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free pointer to MOBIBuffer structure and pointer to data
|
||||
|
||||
Free data initialized with mobi_buffer_init();
|
||||
|
||||
@param[in] buf MOBIBuffer structure
|
||||
*/
|
||||
void mobi_buffer_free(MOBIBuffer *buf) {
|
||||
if (buf == NULL) { return; }
|
||||
if (buf->data != NULL) {
|
||||
free(buf->data);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free pointer to MOBIBuffer structure
|
||||
|
||||
Free data initialized with mobi_buffer_init_null();
|
||||
Unlike mobi_buffer_free() it will not free pointer to buf->data
|
||||
|
||||
@param[in] buf MOBIBuffer structure
|
||||
*/
|
||||
void mobi_buffer_free_null(MOBIBuffer *buf) {
|
||||
if (buf == NULL) { return; }
|
||||
free(buf);
|
||||
}
|
||||
58
app/src/main/cpp/libmobi/src/buffer.h
vendored
Normal file
58
app/src/main/cpp/libmobi/src/buffer.h
vendored
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/** @file buffer.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_buffer_h
|
||||
#define libmobi_buffer_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
/**
|
||||
@brief Buffer to read to/write from
|
||||
*/
|
||||
typedef struct {
|
||||
size_t offset; /**< Current offset in respect to buffer start */
|
||||
size_t maxlen; /**< Length of the buffer data */
|
||||
unsigned char *data; /**< Pointer to buffer data */
|
||||
MOBI_RET error; /**< MOBI_SUCCESS = 0 if operation on buffer is successful, non-zero value on failure */
|
||||
} MOBIBuffer;
|
||||
|
||||
MOBIBuffer * mobi_buffer_init(const size_t len);
|
||||
MOBIBuffer * mobi_buffer_init_null(unsigned char *data, const size_t len);
|
||||
void mobi_buffer_resize(MOBIBuffer *buf, const size_t newlen);
|
||||
void mobi_buffer_add8(MOBIBuffer *buf, const uint8_t data);
|
||||
void mobi_buffer_add16(MOBIBuffer *buf, const uint16_t data);
|
||||
void mobi_buffer_add32(MOBIBuffer *buf, const uint32_t data);
|
||||
void mobi_buffer_addraw(MOBIBuffer *buf, const unsigned char* data, const size_t len);
|
||||
void mobi_buffer_addstring(MOBIBuffer *buf, const char *str);
|
||||
void mobi_buffer_addzeros(MOBIBuffer *buf, const size_t count);
|
||||
uint8_t mobi_buffer_get8(MOBIBuffer *buf);
|
||||
uint16_t mobi_buffer_get16(MOBIBuffer *buf);
|
||||
uint32_t mobi_buffer_get32(MOBIBuffer *buf);
|
||||
uint32_t mobi_buffer_get_varlen(MOBIBuffer *buf, size_t *len);
|
||||
uint32_t mobi_buffer_get_varlen_dec(MOBIBuffer *buf, size_t *len);
|
||||
void mobi_buffer_dup8(uint8_t **val, MOBIBuffer *buf);
|
||||
void mobi_buffer_dup16(uint16_t **val, MOBIBuffer *buf);
|
||||
void mobi_buffer_dup32(uint32_t **val, MOBIBuffer *buf);
|
||||
void mobi_buffer_getstring(char *str, MOBIBuffer *buf, const size_t len);
|
||||
void mobi_buffer_appendstring(char *str, MOBIBuffer *buf, const size_t len);
|
||||
void mobi_buffer_getraw(void *data, MOBIBuffer *buf, const size_t len);
|
||||
unsigned char * mobi_buffer_getpointer(MOBIBuffer *buf, const size_t len);
|
||||
void mobi_buffer_copy8(MOBIBuffer *dest, MOBIBuffer *source);
|
||||
void mobi_buffer_move(MOBIBuffer *buf, const int offset, const size_t len);
|
||||
void mobi_buffer_copy(MOBIBuffer *dest, MOBIBuffer *source, const size_t len);
|
||||
bool mobi_buffer_match_magic(MOBIBuffer *buf, const char *magic);
|
||||
bool mobi_buffer_match_magic_offset(MOBIBuffer *buf, const char *magic, const size_t offset);
|
||||
void mobi_buffer_seek(MOBIBuffer *buf, const int diff);
|
||||
void mobi_buffer_setpos(MOBIBuffer *buf, const size_t pos);
|
||||
void mobi_buffer_free(MOBIBuffer *buf);
|
||||
void mobi_buffer_free_null(MOBIBuffer *buf);
|
||||
|
||||
#endif
|
||||
221
app/src/main/cpp/libmobi/src/compression.c
vendored
Normal file
221
app/src/main/cpp/libmobi/src/compression.c
vendored
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/** @file compression.c
|
||||
* @brief Functions handling compression
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "compression.h"
|
||||
#include "buffer.h"
|
||||
#include "mobi.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
||||
/**
|
||||
@brief Decompressor fo PalmDOC version of LZ77 compression
|
||||
|
||||
Decompressor based on this algorithm:
|
||||
http://en.wikibooks.org/wiki/Data_Compression/Dictionary_compression#PalmDoc
|
||||
|
||||
@param[out] out Decompressed destination data
|
||||
@param[in] in Compressed source data
|
||||
@param[in,out] len_out Size of the memory reserved for decompressed data.
|
||||
On return it is set to actual size of decompressed data
|
||||
@param[in] len_in Size of compressed data
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_decompress_lz77(unsigned char *out, const unsigned char *in, size_t *len_out, const size_t len_in) {
|
||||
MOBI_RET ret = MOBI_SUCCESS;
|
||||
MOBIBuffer *buf_in = mobi_buffer_init_null((unsigned char *) in, len_in);
|
||||
if (buf_in == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
MOBIBuffer *buf_out = mobi_buffer_init_null(out, *len_out);
|
||||
if (buf_out == NULL) {
|
||||
mobi_buffer_free_null(buf_in);
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
while (ret == MOBI_SUCCESS && buf_in->offset < buf_in->maxlen) {
|
||||
uint8_t byte = mobi_buffer_get8(buf_in);
|
||||
/* byte pair: space + char */
|
||||
if (byte >= 0xc0) {
|
||||
mobi_buffer_add8(buf_out, ' ');
|
||||
mobi_buffer_add8(buf_out, byte ^ 0x80);
|
||||
}
|
||||
/* length, distance pair */
|
||||
/* 0x8000 + (distance << 3) + ((length-3) & 0x07) */
|
||||
else if (byte >= 0x80) {
|
||||
uint8_t next = mobi_buffer_get8(buf_in);
|
||||
uint16_t distance = ((((byte << 8) | ((uint8_t)next)) >> 3) & 0x7ff);
|
||||
uint8_t length = (next & 0x7) + 3;
|
||||
while (length--) {
|
||||
mobi_buffer_move(buf_out, -distance, 1);
|
||||
}
|
||||
}
|
||||
/* single char, not modified */
|
||||
else if (byte >= 0x09) {
|
||||
mobi_buffer_add8(buf_out, byte);
|
||||
}
|
||||
/* val chars not modified */
|
||||
else if (byte >= 0x01) {
|
||||
mobi_buffer_copy(buf_out, buf_in, byte);
|
||||
}
|
||||
/* char '\0', not modified */
|
||||
else {
|
||||
mobi_buffer_add8(buf_out, byte);
|
||||
}
|
||||
if (buf_in->error || buf_out->error) {
|
||||
ret = MOBI_BUFFER_END;
|
||||
}
|
||||
}
|
||||
*len_out = buf_out->offset;
|
||||
mobi_buffer_free_null(buf_out);
|
||||
mobi_buffer_free_null(buf_in);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read at most 8 bytes from buffer, big-endian
|
||||
|
||||
If buffer data is shorter returned value is padded with zeroes
|
||||
|
||||
@param[in] buf MOBIBuffer structure to read from
|
||||
@return 64-bit value
|
||||
*/
|
||||
static MOBI_INLINE uint64_t mobi_buffer_fill64(MOBIBuffer *buf) {
|
||||
uint64_t val = 0;
|
||||
uint8_t i = 8;
|
||||
size_t bytesleft = buf->maxlen - buf->offset;
|
||||
unsigned char *ptr = buf->data + buf->offset;
|
||||
while (i-- && bytesleft--) {
|
||||
val |= (uint64_t) *ptr++ << (i * 8);
|
||||
}
|
||||
/* increase counter by 4 bytes only, 4 bytes overlap on each call */
|
||||
buf->offset += 4;
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Internal function for huff/cdic decompression
|
||||
|
||||
Decompressor and HUFF/CDIC records parsing based on:
|
||||
perl EBook::Tools::Mobipocket
|
||||
python mobiunpack.py, calibre
|
||||
|
||||
@param[out] buf_out MOBIBuffer structure with decompressed data
|
||||
@param[in] buf_in MOBIBuffer structure with compressed data
|
||||
@param[in] huffcdic MOBIHuffCdic structure with parsed data from huff/cdic records
|
||||
@param[in] depth Depth of current recursion level
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_decompress_huffman_internal(MOBIBuffer *buf_out, MOBIBuffer *buf_in, const MOBIHuffCdic *huffcdic, size_t depth) {
|
||||
if (depth > MOBI_HUFFMAN_MAXDEPTH) {
|
||||
debug_print("Too many levels of recursion: %zu\n", depth);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
MOBI_RET ret = MOBI_SUCCESS;
|
||||
int8_t bitcount = 32;
|
||||
/* this cast should be safe: max record size is 4096 */
|
||||
int bitsleft = (int) (buf_in->maxlen * 8);
|
||||
uint8_t code_length = 0;
|
||||
uint64_t buffer = mobi_buffer_fill64(buf_in);
|
||||
while (ret == MOBI_SUCCESS) {
|
||||
if (bitcount <= 0) {
|
||||
bitcount += 32;
|
||||
buffer = mobi_buffer_fill64(buf_in);
|
||||
}
|
||||
uint32_t code = (buffer >> bitcount) & 0xffffffffU;
|
||||
/* lookup code in table1 */
|
||||
uint32_t t1 = huffcdic->table1[code >> 24];
|
||||
/* get maxcode and codelen from t1 */
|
||||
code_length = t1 & 0x1f;
|
||||
uint32_t maxcode = (((t1 >> 8) + 1) << (32 - code_length)) - 1;
|
||||
/* check termination bit */
|
||||
if (!(t1 & 0x80)) {
|
||||
/* get offset from mincode, maxcode tables */
|
||||
while (code < huffcdic->mincode_table[code_length]) {
|
||||
if (++code_length >= HUFF_CODETABLE_SIZE) {
|
||||
debug_print("Wrong offset to mincode table: %hhu\n", code_length);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
}
|
||||
maxcode = huffcdic->maxcode_table[code_length];
|
||||
}
|
||||
bitcount -= code_length;
|
||||
bitsleft -= code_length;
|
||||
if (bitsleft < 0) {
|
||||
break;
|
||||
}
|
||||
/* get index for symbol offset */
|
||||
uint32_t index = (uint32_t) (maxcode - code) >> (32 - code_length);
|
||||
/* check which part of cdic to use */
|
||||
uint16_t cdic_index = (uint16_t) ((uint32_t)index >> huffcdic->code_length);
|
||||
if (index >= huffcdic->index_count) {
|
||||
debug_print("Wrong symbol offsets index: %u\n", index);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* get offset */
|
||||
uint32_t offset = huffcdic->symbol_offsets[index];
|
||||
uint32_t symbol_length = (uint32_t) huffcdic->symbols[cdic_index][offset] << 8 | (uint32_t) huffcdic->symbols[cdic_index][offset + 1];
|
||||
/* 1st bit is is_decompressed flag */
|
||||
int is_decompressed = symbol_length >> 15;
|
||||
/* get rid of flag */
|
||||
symbol_length &= 0x7fff;
|
||||
if (is_decompressed) {
|
||||
/* symbol is at (offset + 2), 2 bytes used earlier for symbol length */
|
||||
mobi_buffer_addraw(buf_out, (huffcdic->symbols[cdic_index] + offset + 2), symbol_length);
|
||||
ret = buf_out->error;
|
||||
} else {
|
||||
/* symbol is compressed */
|
||||
/* TODO cache uncompressed symbols? */
|
||||
MOBIBuffer buf_sym;
|
||||
buf_sym.data = huffcdic->symbols[cdic_index] + offset + 2;
|
||||
buf_sym.offset = 0;
|
||||
buf_sym.maxlen = symbol_length;
|
||||
buf_sym.error = MOBI_SUCCESS;
|
||||
ret = mobi_decompress_huffman_internal(buf_out, &buf_sym, huffcdic, depth + 1);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Decompressor for huff/cdic compressed text records
|
||||
|
||||
Decompressor and HUFF/CDIC records parsing based on:
|
||||
perl EBook::Tools::Mobipocket
|
||||
python mobiunpack.py, calibre
|
||||
|
||||
@param[out] out Decompressed destination data
|
||||
@param[in] in Compressed source data
|
||||
@param[in,out] len_out Size of the memory reserved for decompressed data.
|
||||
On return it is set to actual size of decompressed data
|
||||
@param[in] len_in Size of compressed data
|
||||
@param[in] huffcdic MOBIHuffCdic structure with parsed data from huff/cdic records
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_decompress_huffman(unsigned char *out, const unsigned char *in, size_t *len_out, size_t len_in, const MOBIHuffCdic *huffcdic) {
|
||||
MOBIBuffer *buf_in = mobi_buffer_init_null((unsigned char *) in, len_in);
|
||||
if (buf_in == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
MOBIBuffer *buf_out = mobi_buffer_init_null(out, *len_out);
|
||||
if (buf_out == NULL) {
|
||||
mobi_buffer_free_null(buf_in);
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
MOBI_RET ret = mobi_decompress_huffman_internal(buf_out, buf_in, huffcdic, 0);
|
||||
*len_out = buf_out->offset;
|
||||
mobi_buffer_free_null(buf_out);
|
||||
mobi_buffer_free_null(buf_in);
|
||||
return ret;
|
||||
}
|
||||
43
app/src/main/cpp/libmobi/src/compression.h
vendored
Normal file
43
app/src/main/cpp/libmobi/src/compression.h
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/** @file compression.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_compression_h
|
||||
#define libmobi_compression_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
#ifndef MOBI_INLINE
|
||||
#define MOBI_INLINE /**< Syntax for compiler inline keyword from config.h */
|
||||
#endif
|
||||
|
||||
/* FIXME: what is the reasonable value? */
|
||||
#define MOBI_HUFFMAN_MAXDEPTH 20 /**< Maximal recursion level for huffman decompression routine */
|
||||
#define HUFF_CODETABLE_SIZE 33 /**< Size of min- and maxcode tables */
|
||||
|
||||
|
||||
/**
|
||||
@brief Parsed data from HUFF and CDIC records needed to unpack huffman compressed text
|
||||
*/
|
||||
typedef struct {
|
||||
size_t index_count; /**< Total number of indices in all CDIC records, stored in each CDIC record header */
|
||||
size_t index_read; /**< Number of indices parsed, used by parser */
|
||||
size_t code_length; /**< Code length value stored in CDIC record header */
|
||||
uint32_t table1[256]; /**< Table of big-endian indices from HUFF record data1 */
|
||||
uint32_t mincode_table[HUFF_CODETABLE_SIZE]; /**< Table of big-endian mincodes from HUFF record data2 */
|
||||
uint32_t maxcode_table[HUFF_CODETABLE_SIZE]; /**< Table of big-endian maxcodes from HUFF record data2 */
|
||||
uint16_t *symbol_offsets; /**< Index of symbol offsets parsed from CDIC records (index_count entries) */
|
||||
unsigned char **symbols; /**< Array of pointers to start of symbols data in each CDIC record (index = number of CDIC record) */
|
||||
} MOBIHuffCdic;
|
||||
|
||||
MOBI_RET mobi_decompress_lz77(unsigned char *out, const unsigned char *in, size_t *len_out, const size_t len_in);
|
||||
MOBI_RET mobi_decompress_huffman(unsigned char *out, const unsigned char *in, size_t *len_out, size_t len_in, const MOBIHuffCdic *huffcdic);
|
||||
|
||||
#endif
|
||||
18
app/src/main/cpp/libmobi/src/config.h
vendored
Normal file
18
app/src/main/cpp/libmobi/src/config.h
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/** @file src/config.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_config_h
|
||||
#define mobi_config_h
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../config.h"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
159
app/src/main/cpp/libmobi/src/debug.c
vendored
Normal file
159
app/src/main/cpp/libmobi/src/debug.c
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/** @file debug.c
|
||||
* @brief Debugging functions, enable by running configure --enable-debug
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "debug.h"
|
||||
#include "index.h"
|
||||
|
||||
/**
|
||||
@brief Debugging wrapper for free(void *ptr)
|
||||
|
||||
@param[in] ptr Pointer
|
||||
@param[in] file Calling file
|
||||
@param[in] line Calling line
|
||||
*/
|
||||
void debug_free(void *ptr, const char *file, const int line) {
|
||||
printf("%s:%d: free(%p)\n",file, line, ptr);
|
||||
(free)(ptr);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Debugging wrapper for malloc(size_t size)
|
||||
|
||||
@param[in] size Size of memory
|
||||
@param[in] file Calling file
|
||||
@param[in] line Calling line
|
||||
@return A pointer to the allocated memory block on success, NULL on failure
|
||||
|
||||
*/
|
||||
void *debug_malloc(const size_t size, const char *file, const int line) {
|
||||
void *ptr = (malloc)(size);
|
||||
printf("%s:%d: malloc(%d)=%p\n", file, line, (int)size, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Debugging wrapper for realloc(void* ptr, size_t size)
|
||||
|
||||
@param[in] ptr Pointer
|
||||
@param[in] size Size of memory
|
||||
@param[in] file Calling file
|
||||
@param[in] line Calling line
|
||||
@return A pointer to the reallocated memory block on success, NULL on failure
|
||||
*/
|
||||
void *debug_realloc(void *ptr, const size_t size, const char *file, const int line) {
|
||||
printf("%s:%d: realloc(%p", file, line, ptr);
|
||||
void *rptr = (realloc)(ptr, size);
|
||||
printf(", %d)=%p\n", (int)size, rptr);
|
||||
return rptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Debugging wrapper for calloc(size_t num, size_t size)
|
||||
|
||||
@param[in] num Number of elements to allocate
|
||||
@param[in] size Size of each element
|
||||
@param[in] file Calling file
|
||||
@param[in] line Calling line
|
||||
@return A pointer to the allocated memory block on success, NULL on failure
|
||||
*/
|
||||
void *debug_calloc(const size_t num, const size_t size, const char *file, const int line) {
|
||||
void *ptr = (calloc)(num, size);
|
||||
printf("%s:%d: calloc(%d, %d)=%p\n", file, line, (int)num, (int)size, ptr);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Dump index values
|
||||
|
||||
@param[in] indx Parsed index
|
||||
*/
|
||||
void print_indx(const MOBIIndx *indx) {
|
||||
if (indx == NULL) { return; }
|
||||
for (size_t i = 0; i < indx->entries_count; i++) {
|
||||
MOBIIndexEntry e = indx->entries[i];
|
||||
printf("entry[%zu]: \"%s\"\n", i, e.label);
|
||||
for (size_t j = 0; j < e.tags_count; j++) {
|
||||
MOBIIndexTag t = e.tags[j];
|
||||
printf(" tag[%zu] ", t.tagid);
|
||||
for (size_t k = 0; k < t.tagvalues_count; k++) {
|
||||
printf("[%u] ", t.tagvalues[k]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Dump inflections index (old version)
|
||||
|
||||
@param[in] indx Parsed index
|
||||
*/
|
||||
void print_indx_infl_old(const MOBIIndx *indx) {
|
||||
if (indx == NULL) { return; }
|
||||
for (size_t i = 0; i < indx->entries_count; i++) {
|
||||
MOBIIndexEntry e = indx->entries[i];
|
||||
printf("entry[%zu]: \"%s\"\n", i, e.label);
|
||||
for (size_t j = 0; j < e.tags_count; j++) {
|
||||
MOBIIndexTag t = e.tags[j];
|
||||
printf(" tag[%zu] ", t.tagid);
|
||||
if (t.tagid == 7) {
|
||||
for (size_t k = 0; k < t.tagvalues_count; k += 2) {
|
||||
uint32_t len = t.tagvalues[k];
|
||||
uint32_t offset = t.tagvalues[k + 1];
|
||||
char *string = mobi_get_cncx_string_flat(indx->cncx_record, offset, len);
|
||||
if (string) {
|
||||
printf("\"%s\" [%u] [%u]", string, len, offset);
|
||||
free(string);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t k = 0; k < t.tagvalues_count; k++) {
|
||||
printf("[%u] ", t.tagvalues[k]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Dump orthographic index (old version)
|
||||
|
||||
@param[in] indx Parsed index
|
||||
*/
|
||||
void print_indx_orth_old(const MOBIIndx *indx) {
|
||||
if (indx == NULL) { return; }
|
||||
for (size_t i = 0; i < indx->entries_count; i++) {
|
||||
MOBIIndexEntry e = indx->entries[i];
|
||||
printf("entry[%zu]: \"%s\"\n", i, e.label);
|
||||
for (size_t j = 0; j < e.tags_count; j++) {
|
||||
MOBIIndexTag t = e.tags[j];
|
||||
printf(" tag[%zu] ", t.tagid);
|
||||
if (t.tagid >= 69) {
|
||||
for (size_t k = 0; k < t.tagvalues_count; k++) {
|
||||
uint32_t offset = t.tagvalues[k];
|
||||
char *string = mobi_get_cncx_string(indx->cncx_record, offset);
|
||||
if (string) {
|
||||
printf("\"%s\" [%u] ", string, t.tagvalues[k]);
|
||||
free(string);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t k = 0; k < t.tagvalues_count; k++) {
|
||||
printf("[%u] ", t.tagvalues[k]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
57
app/src/main/cpp/libmobi/src/debug.h
vendored
Normal file
57
app/src/main/cpp/libmobi/src/debug.h
vendored
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/** @file debug.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_debug_h
|
||||
#define libmobi_debug_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
#ifndef MOBI_DEBUG
|
||||
#define MOBI_DEBUG 0 /**< Turn on debugging, set this on by running "configure --enable-debug" */
|
||||
#endif
|
||||
|
||||
#if MOBI_DEBUG_ALLOC
|
||||
/**
|
||||
@defgroup mobi_debug Debug wrappers for memory allocation functions
|
||||
|
||||
Set this on by running "configure --enable-debug-alloc"
|
||||
@{
|
||||
*/
|
||||
#define free(x) debug_free(x, __FILE__, __LINE__)
|
||||
#define malloc(x) debug_malloc(x, __FILE__, __LINE__)
|
||||
#define realloc(x, y) debug_realloc(x, y, __FILE__, __LINE__)
|
||||
#define calloc(x, y) debug_calloc(x, y, __FILE__, __LINE__)
|
||||
/** @} */
|
||||
#endif
|
||||
|
||||
void debug_free(void *ptr, const char *file, const int line);
|
||||
void *debug_malloc(const size_t size, const char *file, const int line);
|
||||
void *debug_realloc(void *ptr, const size_t size, const char *file, const int line);
|
||||
void *debug_calloc(const size_t num, const size_t size, const char *file, const int line);
|
||||
void print_indx(const MOBIIndx *indx);
|
||||
void print_indx_infl_old(const MOBIIndx *indx);
|
||||
void print_indx_orth_old(const MOBIIndx *indx);
|
||||
|
||||
/**
|
||||
@brief Macro for printing debug info to stderr. Wrapper for fprintf
|
||||
@param[in] fmt Format
|
||||
@param[in] ... Additional arguments
|
||||
*/
|
||||
#if (MOBI_DEBUG)
|
||||
#define debug_print(fmt, ...) { \
|
||||
fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
|
||||
__LINE__, __func__, __VA_ARGS__); \
|
||||
}
|
||||
#else
|
||||
#define debug_print(fmt, ...)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1515
app/src/main/cpp/libmobi/src/encryption.c
vendored
Normal file
1515
app/src/main/cpp/libmobi/src/encryption.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
47
app/src/main/cpp/libmobi/src/encryption.h
vendored
Normal file
47
app/src/main/cpp/libmobi/src/encryption.h
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/** @file encryption.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_encryption_h
|
||||
#define mobi_encryption_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
#include "buffer.h"
|
||||
|
||||
/**
|
||||
@brief Drm cookie data
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char *pid; /**< PIDs for decryption, NULL if not set */
|
||||
uint32_t valid_from; /**< validity period start time, unix time in minutes, 0 if not set */
|
||||
uint32_t valid_to; /**< validity period end time, unix time in minutes, MOBI_NOTSET if not set */
|
||||
} MOBICookie;
|
||||
|
||||
/**
|
||||
@brief Drm data
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
unsigned char *key; /**< key for decryption, NULL if not set */
|
||||
uint32_t cookies_count; /**< Cookies count */
|
||||
MOBICookie **cookies; /**< DRM cookie */
|
||||
} MOBIDrm;
|
||||
|
||||
void mobi_free_drm(MOBIData *m);
|
||||
MOBI_RET mobi_buffer_decrypt(unsigned char *out, const unsigned char *in, const size_t length, const MOBIData *m);
|
||||
MOBI_RET mobi_drmkey_set(MOBIData *m, const char *pid);
|
||||
MOBI_RET mobi_drmkey_set_serial(MOBIData *m, const char *serial);
|
||||
MOBI_RET mobi_drmkey_delete(MOBIData *m);
|
||||
MOBI_RET mobi_voucher_add(MOBIData *m, const char *serial, const time_t valid_from, const time_t valid_to,
|
||||
const MOBIExthTag *tamperkeys, const size_t tamperkeys_count);
|
||||
MOBI_RET mobi_drm_serialize_v1(MOBIBuffer *buf, const MOBIData *m);
|
||||
MOBI_RET mobi_drm_serialize_v2(MOBIBuffer *buf, const MOBIData *m);
|
||||
|
||||
#endif /* defined(mobi_encryption_h) */
|
||||
1092
app/src/main/cpp/libmobi/src/index.c
vendored
Normal file
1092
app/src/main/cpp/libmobi/src/index.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
128
app/src/main/cpp/libmobi/src/index.h
vendored
Normal file
128
app/src/main/cpp/libmobi/src/index.h
vendored
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
/** @file index.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_index_h
|
||||
#define mobi_index_h
|
||||
|
||||
#include "config.h"
|
||||
#include "structure.h"
|
||||
#include "mobi.h"
|
||||
|
||||
/**
|
||||
@defgroup index_tag Predefined tag arrays: {tagid, tagindex} for mobi_get_indxentry_tagvalue()
|
||||
@{
|
||||
*/
|
||||
#define INDX_TAG_GUIDE_TITLE_CNCX (unsigned[]) {1, 0} /**< Guide title CNCX offset */
|
||||
|
||||
#define INDX_TAG_NCX_FILEPOS (unsigned[]) {1, 0} /**< NCX filepos offset */
|
||||
#define INDX_TAG_NCX_TEXT_CNCX (unsigned[]) {3, 0} /**< NCX text CNCX offset */
|
||||
#define INDX_TAG_NCX_LEVEL (unsigned[]) {4, 0} /**< NCX level */
|
||||
#define INDX_TAG_NCX_KIND_CNCX (unsigned[]) {5, 0} /**< NCX kind CNCX offset */
|
||||
#define INDX_TAG_NCX_POSFID (unsigned[]) {6, 0} /**< NCX pos:fid */
|
||||
#define INDX_TAG_NCX_POSOFF (unsigned[]) {6, 1} /**< NCX pos:off */
|
||||
#define INDX_TAG_NCX_PARENT (unsigned[]) {21, 0} /**< NCX parent */
|
||||
#define INDX_TAG_NCX_CHILD_START (unsigned[]) {22, 0} /**< NCX start child */
|
||||
#define INDX_TAG_NCX_CHILD_END (unsigned[]) {23, 0} /**< NCX last child */
|
||||
|
||||
#define INDX_TAG_SKEL_COUNT (unsigned[]) {1, 0} /**< Skel fragments count */
|
||||
#define INDX_TAG_SKEL_POSITION (unsigned[]) {6, 0} /**< Skel position */
|
||||
#define INDX_TAG_SKEL_LENGTH (unsigned[]) {6, 1} /**< Skel length */
|
||||
|
||||
#define INDX_TAG_FRAG_AID_CNCX (unsigned[]) {2, 0} /**< Frag aid CNCX offset */
|
||||
#define INDX_TAG_FRAG_FILE_NR (unsigned[]) {3, 0} /**< Frag file number */
|
||||
#define INDX_TAG_FRAG_SEQUENCE_NR (unsigned[]) {4, 0} /**< Frag sequence number */
|
||||
#define INDX_TAG_FRAG_POSITION (unsigned[]) {6, 0} /**< Frag position */
|
||||
#define INDX_TAG_FRAG_LENGTH (unsigned[]) {6, 1} /**< Frag length */
|
||||
|
||||
#define INDX_TAG_ORTH_POSITION (unsigned[]) {1, 0} /**< Orth entry start position */
|
||||
#define INDX_TAG_ORTH_LENGTH (unsigned[]) {2, 0} /**< Orth entry end position */
|
||||
|
||||
#define INDX_TAGARR_ORTH_INFL 42 /**< Inflection groups for orth entry */
|
||||
#define INDX_TAGARR_INFL_GROUPS 5 /**< Inflection groups in infl index */
|
||||
#define INDX_TAGARR_INFL_PARTS_V2 26 /**< Inflection particles in infl index */
|
||||
|
||||
#define INDX_TAGARR_INFL_PARTS_V1 7 /**< Inflection particles in old type infl index */
|
||||
/** @} */
|
||||
|
||||
#define INDX_LABEL_SIZEMAX 1000 /**< Max size of index label */
|
||||
#define INDX_INFLTAG_SIZEMAX 25000 /**< Max size of inflections tags per entry */
|
||||
#define INDX_INFLBUF_SIZEMAX 500 /**< Max size of index label */
|
||||
#define INDX_INFLSTRINGS_MAX 500 /**< Max number of inflected strings */
|
||||
#define ORDT_RECORD_MAXCNT 256 /* max entries count in old ordt */
|
||||
#define CNCX_RECORD_MAXCNT 0xf /* max entries count */
|
||||
#define INDX_RECORD_MAXCNT 10000 /* max index entries per record */
|
||||
#define INDX_TOTAL_MAXCNT ((size_t) INDX_RECORD_MAXCNT * 0xffff) /* max total index entries */
|
||||
#define INDX_NAME_SIZEMAX 0xff
|
||||
|
||||
/**
|
||||
@brief Maximum value of tag values in index entry (MOBIIndexTag)
|
||||
*/
|
||||
#define INDX_TAGVALUES_MAX 100
|
||||
|
||||
/**
|
||||
@brief Tag entries in TAGX section (for internal INDX parsing)
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t tag; /**< Tag */
|
||||
uint8_t values_count; /**< Number of values */
|
||||
uint8_t bitmask; /**< Bitmask */
|
||||
uint8_t control_byte; /**< EOF control byte */
|
||||
} TAGXTags;
|
||||
|
||||
/**
|
||||
@brief Parsed TAGX section (for internal INDX parsing)
|
||||
|
||||
TAGX tags hold metadata of index entries.
|
||||
It is present in the first index record.
|
||||
*/
|
||||
typedef struct {
|
||||
TAGXTags *tags; /**< Array of tag entries */
|
||||
size_t tags_count; /**< Number of tag entries */
|
||||
size_t control_byte_count; /**< Number of control bytes */
|
||||
} MOBITagx;
|
||||
|
||||
/**
|
||||
@brief Parsed IDXT section (for internal INDX parsing)
|
||||
|
||||
IDXT section holds offsets to index entries
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t *offsets; /**< Offsets to index entries */
|
||||
size_t offsets_count; /**< Offsets count */
|
||||
} MOBIIdxt;
|
||||
|
||||
/**
|
||||
@brief Parsed ORDT sections (for internal INDX parsing)
|
||||
|
||||
ORDT sections hold data for decoding index labels.
|
||||
It is mapping of encoded chars to unicode.
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *ordt1; /**< ORDT1 offsets */
|
||||
uint16_t *ordt2; /**< ORDT2 offsets */
|
||||
size_t type; /**< Type (0: 16, 1: 8 bit offsets) */
|
||||
size_t ordt1_pos; /**< Offset of ORDT1 data */
|
||||
size_t ordt2_pos; /**< Offset of ORDT2 data */
|
||||
size_t offsets_count; /**< Offsets count */
|
||||
} MOBIOrdt;
|
||||
|
||||
MOBI_RET mobi_parse_index(const MOBIData *m, MOBIIndx *indx, const size_t indx_record_number);
|
||||
MOBI_RET mobi_parse_indx(const MOBIPdbRecord *indx_record, MOBIIndx *indx, MOBITagx *tagx, MOBIOrdt *ordt);
|
||||
MOBI_RET mobi_get_indxentry_tagvalue(uint32_t *tagvalue, const MOBIIndexEntry *entry, const unsigned tag_arr[]);
|
||||
size_t mobi_get_indxentry_tagarray(uint32_t **tagarr, const MOBIIndexEntry *entry, const size_t tagid);
|
||||
bool mobi_indx_has_tag(const MOBIIndx *indx, const size_t tagid);
|
||||
char * mobi_get_cncx_string(const MOBIPdbRecord *cncx_record, const uint32_t cncx_offset);
|
||||
char * mobi_get_cncx_string_utf8(const MOBIPdbRecord *cncx_record, const uint32_t cncx_offset, MOBIEncoding cncx_encoding);
|
||||
char * mobi_get_cncx_string_flat(const MOBIPdbRecord *cncx_record, const uint32_t cncx_offset, const size_t length);
|
||||
MOBI_RET mobi_decode_infl(unsigned char *decoded, int *decoded_size, const unsigned char *rule);
|
||||
MOBI_RET mobi_trie_insert_infl(MOBITrie **root, const MOBIIndx *indx, size_t i);
|
||||
size_t mobi_trie_get_inflgroups(char **infl_strings, MOBITrie * const root, const char *string);
|
||||
|
||||
#endif
|
||||
444
app/src/main/cpp/libmobi/src/memory.c
vendored
Normal file
444
app/src/main/cpp/libmobi/src/memory.c
vendored
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/** @file memory.c
|
||||
* @brief Functions for initializing and releasing structures and data containers
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "memory.h"
|
||||
#include "debug.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
@brief Initializer for MOBIData structure
|
||||
|
||||
It allocates memory for structure.
|
||||
Memory should be freed with mobi_free().
|
||||
|
||||
@return MOBIData on success, NULL otherwise
|
||||
*/
|
||||
MOBIData * mobi_init(void) {
|
||||
MOBIData *m = NULL;
|
||||
m = calloc(1, sizeof(MOBIData));
|
||||
if (m == NULL) { return NULL; }
|
||||
m->use_kf8 = true;
|
||||
m->kf8_boundary_offset = MOBI_NOTSET;
|
||||
m->drm_key = NULL;
|
||||
m->ph = NULL;
|
||||
m->rh = NULL;
|
||||
m->mh = NULL;
|
||||
m->eh = NULL;
|
||||
m->rec = NULL;
|
||||
m->next = NULL;
|
||||
m->internals = NULL;
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIMobiHeader structure
|
||||
|
||||
@param[in] mh MOBIMobiHeader structure
|
||||
*/
|
||||
void mobi_free_mh(MOBIMobiHeader *mh) {
|
||||
if (mh == NULL) {
|
||||
return;
|
||||
}
|
||||
free(mh->header_length);
|
||||
free(mh->mobi_type);
|
||||
free(mh->text_encoding);
|
||||
free(mh->uid);
|
||||
free(mh->version);
|
||||
free(mh->orth_index);
|
||||
free(mh->infl_index);
|
||||
free(mh->names_index);
|
||||
free(mh->keys_index);
|
||||
free(mh->extra0_index);
|
||||
free(mh->extra1_index);
|
||||
free(mh->extra2_index);
|
||||
free(mh->extra3_index);
|
||||
free(mh->extra4_index);
|
||||
free(mh->extra5_index);
|
||||
free(mh->non_text_index);
|
||||
free(mh->full_name_offset);
|
||||
free(mh->full_name_length);
|
||||
free(mh->locale);
|
||||
free(mh->dict_input_lang);
|
||||
free(mh->dict_output_lang);
|
||||
free(mh->min_version);
|
||||
free(mh->image_index);
|
||||
free(mh->huff_rec_index);
|
||||
free(mh->huff_rec_count);
|
||||
free(mh->datp_rec_index);
|
||||
free(mh->datp_rec_count);
|
||||
free(mh->exth_flags);
|
||||
free(mh->unknown6);
|
||||
free(mh->drm_offset);
|
||||
free(mh->drm_count);
|
||||
free(mh->drm_size);
|
||||
free(mh->drm_flags);
|
||||
free(mh->fdst_index);
|
||||
free(mh->first_text_index);
|
||||
free(mh->last_text_index);
|
||||
free(mh->fdst_section_count);
|
||||
//free(mh->unknown9);
|
||||
free(mh->fcis_index);
|
||||
free(mh->fcis_count);
|
||||
free(mh->flis_index);
|
||||
free(mh->flis_count);
|
||||
free(mh->unknown10);
|
||||
free(mh->unknown11);
|
||||
free(mh->srcs_index);
|
||||
free(mh->srcs_count);
|
||||
free(mh->unknown12);
|
||||
free(mh->unknown13);
|
||||
free(mh->extra_flags);
|
||||
free(mh->ncx_index);
|
||||
free(mh->fragment_index);
|
||||
free(mh->skeleton_index);
|
||||
free(mh->unknown14);
|
||||
free(mh->unknown15);
|
||||
free(mh->datp_index);
|
||||
free(mh->guide_index);
|
||||
free(mh->unknown16);
|
||||
free(mh->unknown17);
|
||||
free(mh->unknown18);
|
||||
free(mh->unknown19);
|
||||
free(mh->unknown20);
|
||||
free(mh->full_name);
|
||||
free(mh);
|
||||
mh = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free all MOBIPdbRecord structures and its respective data attached to MOBIData structure
|
||||
|
||||
Each MOBIPdbRecord structure holds metadata and data for each pdb record
|
||||
|
||||
@param[in,out] m MOBIData structure
|
||||
*/
|
||||
void mobi_free_rec(MOBIData *m) {
|
||||
MOBIPdbRecord *curr, *tmp;
|
||||
curr = m->rec;
|
||||
while (curr != NULL) {
|
||||
tmp = curr;
|
||||
curr = curr->next;
|
||||
free(tmp->data);
|
||||
free(tmp);
|
||||
tmp = NULL;
|
||||
}
|
||||
m->rec = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free all MOBIExthHeader structures and its respective data attached to MOBIData structure
|
||||
|
||||
Each MOBIExthHeader structure holds metadata and data for each EXTH record
|
||||
|
||||
@param[in,out] m MOBIData structure
|
||||
*/
|
||||
void mobi_free_eh(MOBIData *m) {
|
||||
MOBIExthHeader *curr, *tmp;
|
||||
curr = m->eh;
|
||||
while (curr != NULL) {
|
||||
tmp = curr;
|
||||
curr = curr->next;
|
||||
free(tmp->data);
|
||||
free(tmp);
|
||||
tmp = NULL;
|
||||
}
|
||||
m->eh = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIData structure for currenly unused hybrid part and all its children
|
||||
|
||||
@param[in] m MOBIData structure
|
||||
*/
|
||||
void mobi_free_next(MOBIData *m) {
|
||||
if (m && m->next) {
|
||||
mobi_free_mh(m->next->mh);
|
||||
mobi_free_eh(m->next);
|
||||
free(m->next->rh);
|
||||
free(m->next);
|
||||
m->next = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIData structure and all its children
|
||||
|
||||
@param[in] m MOBIData structure
|
||||
*/
|
||||
void mobi_free(MOBIData *m) {
|
||||
if (m == NULL) {
|
||||
return;
|
||||
}
|
||||
mobi_free_mh(m->mh);
|
||||
mobi_free_eh(m);
|
||||
mobi_free_rec(m);
|
||||
free(m->ph);
|
||||
free(m->rh);
|
||||
mobi_free_next(m);
|
||||
mobi_free_internals(m);
|
||||
free(m);
|
||||
m = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Initialize and return MOBIHuffCdic structure.
|
||||
|
||||
MOBIHuffCdic structure holds parsed data from HUFF, CDIC records.
|
||||
It is used for huffman decompression.
|
||||
Initialized structure is a child of MOBIData structure.
|
||||
It must be freed with mobi_free_huffcdic().
|
||||
|
||||
@return MOBIHuffCdic on success, NULL otherwise
|
||||
*/
|
||||
MOBIHuffCdic * mobi_init_huffcdic(void) {
|
||||
MOBIHuffCdic *huffcdic = calloc(1, sizeof(MOBIHuffCdic));
|
||||
if (huffcdic == NULL) {
|
||||
debug_print("%s", "Memory allocation for huffcdic structure failed\n");
|
||||
return NULL;
|
||||
}
|
||||
return huffcdic;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIHuffCdic structure and all its children
|
||||
|
||||
@param[in] huffcdic MOBIData structure
|
||||
*/
|
||||
void mobi_free_huffcdic(MOBIHuffCdic *huffcdic) {
|
||||
if (huffcdic == NULL) {
|
||||
return;
|
||||
}
|
||||
free(huffcdic->symbol_offsets);
|
||||
free(huffcdic->symbols);
|
||||
free(huffcdic);
|
||||
huffcdic = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Initialize and return MOBIRawml structure.
|
||||
|
||||
MOBIRawml structure holds parsed text record metadata.
|
||||
It is used in the process of parsing rawml text data.
|
||||
It must be freed with mobi_free_rawml().
|
||||
|
||||
@param[in] m Initialized MOBIData structure
|
||||
@return MOBIRawml on success, NULL otherwise
|
||||
*/
|
||||
MOBIRawml * mobi_init_rawml(const MOBIData *m) {
|
||||
MOBIRawml *rawml = malloc(sizeof(MOBIRawml));
|
||||
if (rawml == NULL) {
|
||||
debug_print("%s", "Memory allocation failed for rawml structure\n");
|
||||
return NULL;
|
||||
}
|
||||
rawml->version = mobi_get_fileversion(m);
|
||||
rawml->fdst = NULL;
|
||||
rawml->skel = NULL;
|
||||
rawml->frag = NULL;
|
||||
rawml->guide = NULL;
|
||||
rawml->ncx = NULL;
|
||||
rawml->orth = NULL;
|
||||
rawml->infl = NULL;
|
||||
rawml->flow = NULL;
|
||||
rawml->markup = NULL;
|
||||
rawml->resources = NULL;
|
||||
return rawml;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIFdst structure and all its children
|
||||
|
||||
@param[in] fdst MOBIFdst structure
|
||||
*/
|
||||
void mobi_free_fdst(MOBIFdst *fdst) {
|
||||
if (fdst == NULL) {
|
||||
return;
|
||||
}
|
||||
if (fdst->fdst_section_count > 0) {
|
||||
free(fdst->fdst_section_starts);
|
||||
free(fdst->fdst_section_ends);
|
||||
}
|
||||
free(fdst);
|
||||
fdst = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Initialize and return MOBIIndx structure.
|
||||
|
||||
MOBIIndx structure holds INDX index record entries.
|
||||
Must be freed with mobi_free_indx()
|
||||
|
||||
@return MOBIIndx on success, NULL otherwise
|
||||
*/
|
||||
MOBIIndx * mobi_init_indx(void) {
|
||||
MOBIIndx *indx = calloc(1, sizeof(MOBIIndx));
|
||||
if (indx == NULL) {
|
||||
debug_print("%s", "Memory allocation failed for indx structure\n");
|
||||
return NULL;
|
||||
}
|
||||
indx->entries = NULL;
|
||||
indx->cncx_record = NULL;
|
||||
indx->orth_index_name = NULL;
|
||||
return indx;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free index entries data and all its children
|
||||
|
||||
@param[in] indx MOBIIndx structure that holds indx->entries
|
||||
*/
|
||||
void mobi_free_index_entries(MOBIIndx *indx) {
|
||||
if (indx == NULL || indx->entries == NULL) {
|
||||
return;
|
||||
}
|
||||
size_t i = 0;
|
||||
while (i < indx->entries_count) {
|
||||
free(indx->entries[i].label);
|
||||
if (indx->entries[i].tags != NULL) {
|
||||
size_t j = 0;
|
||||
while (j < indx->entries[i].tags_count) {
|
||||
free(indx->entries[i].tags[j++].tagvalues);
|
||||
}
|
||||
free(indx->entries[i].tags);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
free(indx->entries);
|
||||
indx->entries = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIIndx structure and all its children
|
||||
|
||||
@param[in] indx MOBIIndx structure that holds indx->entries
|
||||
*/
|
||||
void mobi_free_indx(MOBIIndx *indx) {
|
||||
if (indx == NULL) {
|
||||
return;
|
||||
}
|
||||
mobi_free_index_entries(indx);
|
||||
if (indx->orth_index_name) {
|
||||
free(indx->orth_index_name);
|
||||
}
|
||||
free(indx);
|
||||
indx = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBITagx structure and all its children
|
||||
|
||||
@param[in] tagx MOBITagx structure
|
||||
*/
|
||||
void mobi_free_tagx(MOBITagx *tagx) {
|
||||
if (tagx == NULL) {
|
||||
return;
|
||||
}
|
||||
free(tagx->tags);
|
||||
free(tagx);
|
||||
tagx = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIOrdt structure and all its children
|
||||
|
||||
@param[in] ordt MOBIOrdt structure
|
||||
*/
|
||||
void mobi_free_ordt(MOBIOrdt *ordt) {
|
||||
if (ordt == NULL) {
|
||||
return;
|
||||
}
|
||||
free(ordt->ordt1);
|
||||
free(ordt->ordt2);
|
||||
free(ordt);
|
||||
ordt = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIPart structure
|
||||
|
||||
Pointer to data may point to memory area also used by record->data.
|
||||
So we need a flag to leave the memory allocated, while freeing MOBIPart structure
|
||||
|
||||
@param[in] part MOBIPart structure
|
||||
@param[in] free_data Flag, if set - a pointer to part->data is also released, otherwise not released
|
||||
*/
|
||||
void mobi_free_part(MOBIPart *part, int free_data) {
|
||||
MOBIPart *curr, *tmp;
|
||||
curr = part;
|
||||
while (curr != NULL) {
|
||||
tmp = curr;
|
||||
curr = curr->next;
|
||||
if (free_data) { free(tmp->data); }
|
||||
free(tmp);
|
||||
}
|
||||
part = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIPart structure for opf and ncx data
|
||||
|
||||
@param[in] part MOBIPart structure
|
||||
*/
|
||||
void mobi_free_opf_data(MOBIPart *part) {
|
||||
while (part != NULL) {
|
||||
if (part->type == T_NCX || part->type == T_OPF) {
|
||||
free(part->data);
|
||||
}
|
||||
part = part->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIPart structure for decoded font data
|
||||
|
||||
@param[in] part MOBIPart structure
|
||||
*/
|
||||
void mobi_free_font_data(MOBIPart *part) {
|
||||
while (part != NULL) {
|
||||
if (part->type == T_OTF || part->type == T_TTF) {
|
||||
free(part->data);
|
||||
}
|
||||
part = part->next;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIRawml structure allocated by mobi_init_rawml()
|
||||
|
||||
Pointer to data may point to memory area also used by record->data.
|
||||
So we need a flag to leave the memory allocated, while freeing MOBIPart structure
|
||||
|
||||
@param[in] rawml MOBIRawml structure
|
||||
*/
|
||||
void mobi_free_rawml(MOBIRawml *rawml) {
|
||||
if (rawml == NULL) {
|
||||
return;
|
||||
}
|
||||
mobi_free_fdst(rawml->fdst);
|
||||
mobi_free_indx(rawml->skel);
|
||||
mobi_free_indx(rawml->frag);
|
||||
mobi_free_indx(rawml->guide);
|
||||
mobi_free_indx(rawml->ncx);
|
||||
mobi_free_indx(rawml->orth);
|
||||
mobi_free_indx(rawml->infl);
|
||||
mobi_free_part(rawml->flow, true);
|
||||
mobi_free_part(rawml->markup,true);
|
||||
/* do not free resources data, these are links to records data */
|
||||
/* only free opf and ncx data */
|
||||
mobi_free_opf_data(rawml->resources);
|
||||
/* and free decoded fonts data */
|
||||
mobi_free_font_data(rawml->resources);
|
||||
mobi_free_part(rawml->resources, false);
|
||||
free(rawml);
|
||||
rawml = NULL;
|
||||
}
|
||||
|
||||
|
||||
33
app/src/main/cpp/libmobi/src/memory.h
vendored
Normal file
33
app/src/main/cpp/libmobi/src/memory.h
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/** @file memory.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_memory_h
|
||||
#define libmobi_memory_h
|
||||
|
||||
#include "config.h"
|
||||
#include "index.h"
|
||||
#include "compression.h"
|
||||
#include "mobi.h"
|
||||
|
||||
void mobi_free_mh(MOBIMobiHeader *mh);
|
||||
void mobi_free_rec(MOBIData *m);
|
||||
void mobi_free_eh(MOBIData *m);
|
||||
void mobi_free_next(MOBIData *m);
|
||||
|
||||
MOBIHuffCdic * mobi_init_huffcdic(void);
|
||||
void mobi_free_huffcdic(MOBIHuffCdic *huffcdic);
|
||||
|
||||
MOBIIndx * mobi_init_indx(void);
|
||||
void mobi_free_indx(MOBIIndx *indx);
|
||||
void mobi_free_tagx(MOBITagx *tagx);
|
||||
void mobi_free_ordt(MOBIOrdt *ordt);
|
||||
void mobi_free_index_entries(MOBIIndx *indx);
|
||||
|
||||
#endif
|
||||
864
app/src/main/cpp/libmobi/src/meta.c
vendored
Normal file
864
app/src/main/cpp/libmobi/src/meta.c
vendored
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
/** @file meta.c
|
||||
* @brief Functions for metadata manipulation
|
||||
*
|
||||
* Copyright (c) 2016 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE 1
|
||||
#ifndef __USE_BSD
|
||||
#define __USE_BSD /* for strdup on linux/glibc */
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include "meta.h"
|
||||
#include "util.h"
|
||||
|
||||
/**
|
||||
@brief Get document metadata from exth string
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@param[in] exth_tag MOBIExthTag
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_exthstring(const MOBIData *m, const MOBIExthTag exth_tag) {
|
||||
char *string = NULL;
|
||||
|
||||
MOBIExthHeader *exth;
|
||||
MOBIExthHeader *start = NULL;
|
||||
while ((exth = mobi_next_exthrecord_by_tag(m, exth_tag, &start))) {
|
||||
char *exth_string = mobi_decode_exthstring(m, exth->data, exth->size);
|
||||
if (string == NULL) {
|
||||
string = exth_string;
|
||||
} else if (exth_string) {
|
||||
const char *separator = "; ";
|
||||
size_t new_length = strlen(string) + strlen(exth_string) + strlen(separator) + 1;
|
||||
char *new = malloc(new_length);
|
||||
if (new == NULL) {
|
||||
free(string);
|
||||
free(exth_string);
|
||||
return NULL;
|
||||
}
|
||||
strcpy(new, string);
|
||||
strcat(new, separator);
|
||||
strcat(new, exth_string);
|
||||
free(string);
|
||||
free(exth_string);
|
||||
string = new;
|
||||
}
|
||||
if (start == NULL) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document title metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_title(const MOBIData *m) {
|
||||
if (m == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *title = mobi_meta_get_exthstring(m, EXTH_UPDATEDTITLE);
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
char fullname[MOBI_TITLE_SIZEMAX + 1];
|
||||
MOBI_RET ret = mobi_get_fullname(m, fullname, MOBI_TITLE_SIZEMAX);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
title = strdup(fullname);
|
||||
} else if (m->ph) {
|
||||
title = strdup(m->ph->name);
|
||||
}
|
||||
return title;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document title metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] title String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_title(MOBIData *m, const char *title) {
|
||||
if (title == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(title), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_UPDATEDTITLE, (uint32_t) size, title);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all title metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_title(MOBIData *m) {
|
||||
if (mobi_exists_mobiheader(m) && m->mh->full_name) {
|
||||
m->mh->full_name[0] = '\0';
|
||||
}
|
||||
if (mobi_is_hybrid(m) && mobi_exists_mobiheader(m->next) && m->next->mh->full_name) {
|
||||
m->next->mh->full_name[0] = '\0';
|
||||
}
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_UPDATEDTITLE);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document title metadata
|
||||
|
||||
Replaces all title metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] title String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_title(MOBIData *m, const char *title) {
|
||||
if (title == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
/* set title in mobi header */
|
||||
MOBI_RET ret = mobi_set_fullname(m, title);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
/* set title in palm header */
|
||||
ret = mobi_set_pdbname(m, title);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
/* set title in exth header */
|
||||
ret = mobi_delete_exthrecord_by_tag(m, EXTH_UPDATEDTITLE);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_title(m, title);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document author metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_author(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_AUTHOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document author metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] author String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_author(MOBIData *m, const char *author) {
|
||||
if (author == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(author), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_AUTHOR, (uint32_t) size, author);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all author metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_author(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_AUTHOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document author metadata
|
||||
|
||||
Replaces all author metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] author String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_author(MOBIData *m, const char *author) {
|
||||
if (author == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_author(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_author(m, author);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document subject metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_subject(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_SUBJECT);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document subject metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] subject String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_subject(MOBIData *m, const char *subject) {
|
||||
if (subject == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(subject), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_SUBJECT, (uint32_t) size, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all subject metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_subject(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_SUBJECT);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document subject metadata
|
||||
|
||||
Replaces all subject metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] subject String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_subject(MOBIData *m, const char *subject) {
|
||||
if (subject == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_subject(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_subject(m, subject);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document publisher metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_publisher(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_PUBLISHER);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document publisher metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] publisher String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_publisher(MOBIData *m, const char *publisher) {
|
||||
if (publisher == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(publisher), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_PUBLISHER, (uint32_t) size, publisher);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all publisher metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_publisher(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_PUBLISHER);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document publisher metadata
|
||||
|
||||
Replaces all publisher metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] publisher String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_publisher(MOBIData *m, const char *publisher) {
|
||||
if (publisher == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_publisher(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_publisher(m, publisher);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document publishing date metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_publishdate(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_PUBLISHINGDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document publishdate metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] publishdate String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_publishdate(MOBIData *m, const char *publishdate) {
|
||||
if (publishdate == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(publishdate), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_PUBLISHINGDATE, (uint32_t) size, publishdate);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all publishdate metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_publishdate(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_PUBLISHINGDATE);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document publishdate metadata
|
||||
|
||||
Replaces all publishdate metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] publishdate String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_publishdate(MOBIData *m, const char *publishdate) {
|
||||
if (publishdate == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_publishdate(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_publishdate(m, publishdate);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document description metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_description(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document description metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] description String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_description(MOBIData *m, const char *description) {
|
||||
if (description == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(description), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_DESCRIPTION, (uint32_t) size, description);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all description metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_description(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_DESCRIPTION);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document description metadata
|
||||
|
||||
Replaces all description metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] description String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_description(MOBIData *m, const char *description) {
|
||||
if (description == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_description(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_description(m, description);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document imprint metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_imprint(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_IMPRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document imprint metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] imprint String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_imprint(MOBIData *m, const char *imprint) {
|
||||
if (imprint == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(imprint), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_IMPRINT, (uint32_t) size, imprint);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all imprint metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_imprint(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_IMPRINT);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document imprint metadata
|
||||
|
||||
Replaces all imprint metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] imprint String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_imprint(MOBIData *m, const char *imprint) {
|
||||
if (imprint == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_imprint(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_imprint(m, imprint);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document contributor metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_contributor(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_CONTRIBUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document contributor metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] contributor String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_contributor(MOBIData *m, const char *contributor) {
|
||||
if (contributor == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(contributor), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_CONTRIBUTOR, (uint32_t) size, contributor);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all contributor metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_contributor(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_CONTRIBUTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document contributor metadata
|
||||
|
||||
Replaces all contributor metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] contributor String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_contributor(MOBIData *m, const char *contributor) {
|
||||
if (contributor == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_contributor(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_contributor(m, contributor);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document review metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_review(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_REVIEW);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document review metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] review String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_review(MOBIData *m, const char *review) {
|
||||
if (review == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(review), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_REVIEW, (uint32_t) size, review);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all review metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_review(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_REVIEW);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document review metadata
|
||||
|
||||
Replaces all review metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] review String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_review(MOBIData *m, const char *review) {
|
||||
if (review == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_review(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_review(m, review);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document copyright metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_copyright(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_RIGHTS);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document copyright metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] copyright String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_copyright(MOBIData *m, const char *copyright) {
|
||||
if (copyright == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(copyright), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_RIGHTS, (uint32_t) size, copyright);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all copyright metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_copyright(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_RIGHTS);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document copyright metadata
|
||||
|
||||
Replaces all copyright metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] copyright String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_copyright(MOBIData *m, const char *copyright) {
|
||||
if (copyright == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_copyright(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_copyright(m, copyright);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document ISBN metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_isbn(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_ISBN);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document isbn metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] isbn String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_isbn(MOBIData *m, const char *isbn) {
|
||||
if (isbn == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(isbn), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_ISBN, (uint32_t) size, isbn);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all isbn metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_isbn(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_ISBN);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document isbn metadata
|
||||
|
||||
Replaces all isbn metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] isbn String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_isbn(MOBIData *m, const char *isbn) {
|
||||
if (isbn == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_isbn(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_isbn(m, isbn);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document ASIN metadata
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_asin(const MOBIData *m) {
|
||||
return mobi_meta_get_exthstring(m, EXTH_ASIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document asin metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] asin String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_asin(MOBIData *m, const char *asin) {
|
||||
if (asin == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(asin), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_ASIN, (uint32_t) size, asin);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all asin metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_asin(MOBIData *m) {
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_ASIN);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document asin metadata
|
||||
|
||||
Replaces all asin metadata with new string
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] asin String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_asin(MOBIData *m, const char *asin) {
|
||||
if (asin == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_asin(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_asin(m, asin);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get document language code metadata
|
||||
|
||||
Locale strings are based on IANA language-subtag registry with some custom Mobipocket modifications.
|
||||
See mobi_locale array.
|
||||
|
||||
Returned string must be deallocated by caller
|
||||
|
||||
@param[in] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
char * mobi_meta_get_language(const MOBIData *m) {
|
||||
if (m == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
char *lang = mobi_meta_get_exthstring(m, EXTH_LANGUAGE);
|
||||
if(lang == NULL && m->mh && m->mh->locale && *m->mh->locale) {
|
||||
const char *locale_string = mobi_get_locale_string(*m->mh->locale);
|
||||
if (locale_string) {
|
||||
lang = strdup(locale_string);
|
||||
}
|
||||
}
|
||||
return lang;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Add document language code metadata
|
||||
|
||||
Locale strings are based on IANA language-subtag registry with some custom Mobipocket modifications.
|
||||
See mobi_locale array.
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] language String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_add_language(MOBIData *m, const char *language) {
|
||||
if (language == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
size_t size = min(strlen(language), UINT32_MAX);
|
||||
return mobi_add_exthrecord(m, EXTH_LANGUAGE, (uint32_t) size, language);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all language code metadata
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_delete_language(MOBIData *m) {
|
||||
if(mobi_exists_mobiheader(m) && m->mh->locale) {
|
||||
*m->mh->locale = 0;
|
||||
}
|
||||
if(mobi_is_hybrid(m) && mobi_exists_mobiheader(m->next) && m->next->mh->locale) {
|
||||
*m->next->mh->locale = 0;
|
||||
}
|
||||
return mobi_delete_exthrecord_by_tag(m, EXTH_LANGUAGE);
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Set document language code metadata
|
||||
|
||||
Replaces all language metadata with new string
|
||||
Locale strings are based on IANA language-subtag registry with some custom Mobipocket modifications.
|
||||
See mobi_locale array.
|
||||
|
||||
@param[in,out] m MOBIData structure with loaded data
|
||||
@param[in] language String value
|
||||
@return Pointer to null terminated string, NULL on failure
|
||||
*/
|
||||
MOBI_RET mobi_meta_set_language(MOBIData *m, const char *language) {
|
||||
if (language == NULL) {
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
MOBI_RET ret = mobi_meta_delete_language(m);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
ret = mobi_meta_add_language(m, language);
|
||||
}
|
||||
if(mobi_exists_mobiheader(m) && m->mh->locale) {
|
||||
*m->mh->locale = (uint32_t) mobi_get_locale_number(language);
|
||||
}
|
||||
if(mobi_is_hybrid(m) && mobi_exists_mobiheader(m->next) && m->next->mh->locale) {
|
||||
*m->next->mh->locale = (uint32_t) mobi_get_locale_number(language);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
17
app/src/main/cpp/libmobi/src/meta.h
vendored
Normal file
17
app/src/main/cpp/libmobi/src/meta.h
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/** @file meta.h
|
||||
*
|
||||
* Copyright (c) 2016 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_meta_h
|
||||
#define libmobi_meta_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
#endif /* libmobi_meta_h */
|
||||
5164
app/src/main/cpp/libmobi/src/miniz.c
vendored
Normal file
5164
app/src/main/cpp/libmobi/src/miniz.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
24
app/src/main/cpp/libmobi/src/miniz.h
vendored
Normal file
24
app/src/main/cpp/libmobi/src/miniz.h
vendored
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/** @file miniz.h
|
||||
* @brief header file for third party miniz.c, zlib replacement
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_miniz_h
|
||||
#define libmobi_miniz_h
|
||||
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#define MINIZ_NO_STDIO
|
||||
#define MINIZ_NO_ARCHIVE_APIS
|
||||
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
||||
#define MINIZ_NO_TIME
|
||||
#define MINIZ_NO_ARCHIVE_WRITING_APIS
|
||||
|
||||
#include "miniz.c"
|
||||
|
||||
#endif
|
||||
623
app/src/main/cpp/libmobi/src/mobi.h
vendored
Normal file
623
app/src/main/cpp/libmobi/src/mobi.h
vendored
Normal file
|
|
@ -0,0 +1,623 @@
|
|||
/** @file mobi.h
|
||||
* @brief Libmobi main header file
|
||||
*
|
||||
* This file is installed with the library.
|
||||
* Include it in your project with "#include <mobi.h>".
|
||||
* See aryan of usage in mobitool.c, mobimeta.c, mobidrm.c
|
||||
*
|
||||
* Copyright (c) 2014-2022 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_mobi_h
|
||||
#define libmobi_mobi_h
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <time.h>
|
||||
|
||||
/** @brief Visibility attributes for symbol export */
|
||||
#if defined (__CYGWIN__) || defined (__MINGW32__)
|
||||
#define MOBI_EXPORT __attribute__((visibility("default"))) __declspec(dllexport) extern
|
||||
#elif defined (_WIN32)
|
||||
#define MOBI_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define MOBI_EXPORT __attribute__((__visibility__("default")))
|
||||
#endif
|
||||
|
||||
/**
|
||||
@brief Usually 32-bit values in mobi records
|
||||
with value 0xffffffff mean "value not set"
|
||||
*/
|
||||
#define MOBI_NOTSET UINT32_MAX
|
||||
|
||||
#define MOBI_ENCRYPTION_NONE 0 /**< Text record encryption type: none */
|
||||
#define MOBI_ENCRYPTION_V1 1 /**< Text record encryption type: old mobipocket */
|
||||
#define MOBI_ENCRYPTION_V2 2 /**< Text record encryption type: mobipocket */
|
||||
|
||||
#define MOBI_COMPRESSION_NONE 1 /**< Text record compression type: none */
|
||||
#define MOBI_COMPRESSION_PALMDOC 2 /**< Text record compression type: palmdoc */
|
||||
#define MOBI_COMPRESSION_HUFFCDIC 17480 /**< Text record compression type: huff/cdic */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
/**
|
||||
@defgroup mobi_enums Exported enums
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief Error codes returned by functions
|
||||
*/
|
||||
typedef enum {
|
||||
MOBI_SUCCESS = 0, /**< Generic success return value */
|
||||
MOBI_ERROR = 1, /**< Generic error return value */
|
||||
MOBI_PARAM_ERR = 2, /**< Wrong function parameter */
|
||||
MOBI_DATA_CORRUPT = 3, /**< Corrupted data */
|
||||
MOBI_FILE_NOT_FOUND = 4, /**< File not found */
|
||||
MOBI_FILE_ENCRYPTED = 5, /**< Unsupported encrypted data */
|
||||
MOBI_FILE_UNSUPPORTED = 6, /**< Unsupported document type */
|
||||
MOBI_MALLOC_FAILED = 7, /**< Memory allocation error */
|
||||
MOBI_INIT_FAILED = 8, /**< Initialization error */
|
||||
MOBI_BUFFER_END = 9, /**< Out of buffer error */
|
||||
MOBI_XML_ERR = 10, /**< XMLwriter error */
|
||||
MOBI_DRM_PIDINV = 11, /**< Invalid DRM PID */
|
||||
MOBI_DRM_KEYNOTFOUND = 12, /**< Key not found */
|
||||
MOBI_DRM_UNSUPPORTED = 13, /**< DRM support not included */
|
||||
MOBI_WRITE_FAILED = 14, /**< Writing to file failed */
|
||||
MOBI_DRM_EXPIRED = 15, /**< DRM expired */
|
||||
MOBI_DRM_RANDOM_ERR = 16 /**< DRM random bytes generation failed */
|
||||
} MOBI_RET;
|
||||
|
||||
/**
|
||||
@brief EXTH record types
|
||||
*/
|
||||
typedef enum {
|
||||
EXTH_NUMERIC = 0,
|
||||
EXTH_STRING = 1,
|
||||
EXTH_BINARY = 2
|
||||
} MOBIExthType;
|
||||
|
||||
/**
|
||||
@brief EXTH record tags
|
||||
*/
|
||||
typedef enum {
|
||||
EXTH_DRMSERVER = 1,
|
||||
EXTH_DRMCOMMERCE = 2,
|
||||
EXTH_DRMEBOOKBASE = 3,
|
||||
|
||||
EXTH_TITLE = 99, /**< <dc:title> */
|
||||
EXTH_AUTHOR = 100, /**< <dc:creator> */
|
||||
EXTH_PUBLISHER = 101, /**< <dc:publisher> */
|
||||
EXTH_IMPRINT = 102, /**< <imprint> */
|
||||
EXTH_DESCRIPTION = 103, /**< <dc:description> */
|
||||
EXTH_ISBN = 104, /**< <dc:identifier opf:scheme="ISBN"> */
|
||||
EXTH_SUBJECT = 105, /**< <dc:subject> */
|
||||
EXTH_PUBLISHINGDATE = 106, /**< <dc:date> */
|
||||
EXTH_REVIEW = 107, /**< <review> */
|
||||
EXTH_CONTRIBUTOR = 108, /**< <dc:contributor> */
|
||||
EXTH_RIGHTS = 109, /**< <dc:rights> */
|
||||
EXTH_SUBJECTCODE = 110, /**< <dc:subject BASICCode="subjectcode"> */
|
||||
EXTH_TYPE = 111, /**< <dc:type> */
|
||||
EXTH_SOURCE = 112, /**< <dc:source> */
|
||||
EXTH_ASIN = 113,
|
||||
EXTH_VERSION = 114,
|
||||
EXTH_SAMPLE = 115,
|
||||
EXTH_STARTREADING = 116, /**< Start reading */
|
||||
EXTH_ADULT = 117, /**< <adult> */
|
||||
EXTH_PRICE = 118, /**< <srp> */
|
||||
EXTH_CURRENCY = 119, /**< <srp currency="currency"> */
|
||||
EXTH_KF8BOUNDARY = 121,
|
||||
EXTH_FIXEDLAYOUT = 122, /**< <fixed-layout> */
|
||||
EXTH_BOOKTYPE = 123, /**< <book-type> */
|
||||
EXTH_ORIENTATIONLOCK = 124, /**< <orientation-lock> */
|
||||
EXTH_COUNTRESOURCES = 125,
|
||||
EXTH_ORIGRESOLUTION = 126, /**< <original-resolution> */
|
||||
EXTH_ZEROGUTTER = 127, /**< <zero-gutter> */
|
||||
EXTH_ZEROMARGIN = 128, /**< <zero-margin> */
|
||||
EXTH_KF8COVERURI = 129,
|
||||
EXTH_RESCOFFSET = 131,
|
||||
EXTH_REGIONMAGNI = 132, /**< <region-mag> */
|
||||
|
||||
EXTH_DICTNAME = 200, /**< <DictionaryVeryShortName> */
|
||||
EXTH_COVEROFFSET = 201, /**< <EmbeddedCover> */
|
||||
EXTH_THUMBOFFSET = 202,
|
||||
EXTH_HASFAKECOVER = 203,
|
||||
EXTH_CREATORSOFT = 204,
|
||||
EXTH_CREATORMAJOR = 205,
|
||||
EXTH_CREATORMINOR = 206,
|
||||
EXTH_CREATORBUILD = 207,
|
||||
EXTH_WATERMARK = 208,
|
||||
EXTH_TAMPERKEYS = 209,
|
||||
|
||||
EXTH_FONTSIGNATURE = 300,
|
||||
|
||||
EXTH_CLIPPINGLIMIT = 401,
|
||||
EXTH_PUBLISHERLIMIT = 402,
|
||||
EXTH_UNK403 = 403,
|
||||
EXTH_TTSDISABLE = 404,
|
||||
EXTH_READFORFREE = 405, // uint32_t, rental related, ReadForFree
|
||||
EXTH_RENTAL = 406, // uint64_t
|
||||
EXTH_UNK407 = 407,
|
||||
EXTH_UNK450 = 450,
|
||||
EXTH_UNK451 = 451,
|
||||
EXTH_UNK452 = 452,
|
||||
EXTH_UNK453 = 453,
|
||||
|
||||
EXTH_DOCTYPE = 501, /**< PDOC - Personal Doc; EBOK - ebook; EBSP - ebook sample; */
|
||||
EXTH_LASTUPDATE = 502,
|
||||
EXTH_UPDATEDTITLE = 503,
|
||||
EXTH_ASIN504 = 504,
|
||||
EXTH_TITLEFILEAS = 508,
|
||||
EXTH_CREATORFILEAS = 517,
|
||||
EXTH_PUBLISHERFILEAS = 522,
|
||||
EXTH_LANGUAGE = 524, /**< <dc:language> */
|
||||
EXTH_ALIGNMENT = 525, /**< <primary-writing-mode> */
|
||||
EXTH_CREATORSTRING = 526,
|
||||
EXTH_PAGEDIR = 527,
|
||||
EXTH_OVERRIDEFONTS = 528, /**< <override-kindle-fonts> */
|
||||
EXTH_SORCEDESC = 529,
|
||||
EXTH_DICTLANGIN = 531,
|
||||
EXTH_DICTLANGOUT = 532,
|
||||
EXTH_INPUTSOURCE = 534,
|
||||
EXTH_CREATORBUILDREV = 535,
|
||||
} MOBIExthTag;
|
||||
|
||||
/**
|
||||
@brief Types of files stored in database records
|
||||
*/
|
||||
typedef enum {
|
||||
T_UNKNOWN, /**< unknown */
|
||||
/* markup */
|
||||
T_HTML, /**< html */
|
||||
T_CSS, /**< css */
|
||||
T_SVG, /**< svg */
|
||||
T_OPF, /**< opf */
|
||||
T_NCX, /**< ncx */
|
||||
/* images */
|
||||
T_JPG, /**< jpg */
|
||||
T_GIF, /**< gif */
|
||||
T_PNG, /**< png */
|
||||
T_BMP, /**< bmp */
|
||||
/* fonts */
|
||||
T_OTF, /**< otf */
|
||||
T_TTF, /**< ttf */
|
||||
/* media */
|
||||
T_MP3, /**< mp3 */
|
||||
T_MPG, /**< mp3 */
|
||||
T_PDF, /**< pdf */
|
||||
/* generic types */
|
||||
T_FONT, /**< encoded font */
|
||||
T_AUDIO, /**< audio resource */
|
||||
T_VIDEO, /**< video resource */
|
||||
T_BREAK /**< end of file */
|
||||
} MOBIFiletype;
|
||||
|
||||
/**
|
||||
@brief Metadata of file types
|
||||
*/
|
||||
typedef struct {
|
||||
MOBIFiletype type; /**< MOBIFiletype type */
|
||||
char extension[5]; /**< file extension */
|
||||
char mime_type[30]; /**< mime-type */
|
||||
} MOBIFileMeta;
|
||||
|
||||
/**
|
||||
@brief Encoding types in MOBI header (offset 28)
|
||||
*/
|
||||
typedef enum {
|
||||
MOBI_CP1252 = 1252, /**< cp-1252 encoding */
|
||||
MOBI_UTF8 = 65001, /**< utf-8 encoding */
|
||||
MOBI_UTF16 = 65002, /**< utf-16 encoding */
|
||||
} MOBIEncoding;
|
||||
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@defgroup raw_structs Exported structures for the raw, unparsed records metadata and data
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief Header of palmdoc database file
|
||||
*/
|
||||
typedef struct {
|
||||
char name[33]; /**< 0: Database name, zero terminated, trimmed title (+author) */
|
||||
uint16_t attributes; /**< 32: Attributes bitfield, PALMDB_ATTRIBUTE_DEFAULT */
|
||||
uint16_t version; /**< 34: File version, PALMDB_VERSION_DEFAULT */
|
||||
uint32_t ctime; /**< 36: Creation time */
|
||||
uint32_t mtime; /**< 40: Modification time */
|
||||
uint32_t btime; /**< 44: Backup time */
|
||||
uint32_t mod_num; /**< 48: Modification number, PALMDB_MODNUM_DEFAULT */
|
||||
uint32_t appinfo_offset; /**< 52: Offset to application info (if present) or zero, PALMDB_APPINFO_DEFAULT */
|
||||
uint32_t sortinfo_offset; /**< 56: Offset to sort info (if present) or zero, PALMDB_SORTINFO_DEFAULT */
|
||||
char type[5]; /**< 60: Database type, zero terminated, PALMDB_TYPE_DEFAULT */
|
||||
char creator[5]; /**< 64: Creator type, zero terminated, PALMDB_CREATOR_DEFAULT */
|
||||
uint32_t uid; /**< 68: Used internally to identify record */
|
||||
uint32_t next_rec; /**< 72: Used only when database is loaded into memory, PALMDB_NEXTREC_DEFAULT */
|
||||
uint16_t rec_count; /**< 76: Number of records in the file */
|
||||
} MOBIPdbHeader;
|
||||
|
||||
/**
|
||||
@brief Metadata and data of a record. All records form a linked list.
|
||||
*/
|
||||
typedef struct MOBIPdbRecord {
|
||||
uint32_t offset; /**< Offset of the record data from the start of the database */
|
||||
size_t size; /**< Calculated size of the record data */
|
||||
uint8_t attributes; /**< Record attributes */
|
||||
uint32_t uid; /**< Record unique id, usually sequential even numbers */
|
||||
unsigned char *data; /**< Record data */
|
||||
struct MOBIPdbRecord *next; /**< Pointer to the next record or NULL */
|
||||
} MOBIPdbRecord;
|
||||
|
||||
/**
|
||||
@brief Metadata and data of a EXTH record. All records form a linked list.
|
||||
*/
|
||||
typedef struct MOBIExthHeader {
|
||||
uint32_t tag; /**< Record tag */
|
||||
uint32_t size; /**< Data size */
|
||||
void *data; /**< Record data */
|
||||
struct MOBIExthHeader *next; /**< Pointer to the next record or NULL */
|
||||
} MOBIExthHeader;
|
||||
|
||||
/**
|
||||
@brief EXTH tag metadata
|
||||
*/
|
||||
typedef struct {
|
||||
MOBIExthTag tag; /**< Record tag id */
|
||||
MOBIExthType type; /**< EXTH_NUMERIC, EXTH_STRING or EXTH_BINARY */
|
||||
char *name; /**< Tag name */
|
||||
} MOBIExthMeta;
|
||||
|
||||
/**
|
||||
@brief Header of the Record 0 meta-record
|
||||
*/
|
||||
typedef struct {
|
||||
/* PalmDOC header (extended), offset 0, length 16 */
|
||||
uint16_t compression_type; /**< 0; 1 == no compression, 2 = PalmDOC compression, 17480 = HUFF/CDIC compression */
|
||||
/* uint16_t unused; // 2; 0 */
|
||||
uint32_t text_length; /**< 4; uncompressed length of the entire text of the book */
|
||||
uint16_t text_record_count; /**< 8; number of PDB records used for the text of the book */
|
||||
uint16_t text_record_size; /**< 10; maximum size of each record containing text, always 4096 */
|
||||
uint16_t encryption_type; /**< 12; 0 == no encryption, 1 = Old Mobipocket Encryption, 2 = Mobipocket Encryption */
|
||||
uint16_t unknown1; /**< 14; usually 0 */
|
||||
} MOBIRecord0Header;
|
||||
|
||||
/**
|
||||
@brief MOBI header which follows Record 0 header
|
||||
|
||||
All MOBI header fields are pointers. Some fields are not present in the header, then the pointer is NULL.
|
||||
*/
|
||||
typedef struct {
|
||||
/* MOBI header, offset 16 */
|
||||
char mobi_magic[5]; /**< 16: M O B I { 77, 79, 66, 73 }, zero terminated */
|
||||
uint32_t *header_length; /**< 20: the length of the MOBI header, including the previous 4 bytes */
|
||||
uint32_t *mobi_type; /**< 24: mobipocket file type */
|
||||
MOBIEncoding *text_encoding; /**< 28: 1252 = CP1252, 65001 = UTF-8 */
|
||||
uint32_t *uid; /**< 32: unique id */
|
||||
uint32_t *version; /**< 36: mobipocket format */
|
||||
uint32_t *orth_index; /**< 40: section number of orthographic meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *infl_index; /**< 44: section number of inflection meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *names_index; /**< 48: section number of names meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *keys_index; /**< 52: section number of keys meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra0_index; /**< 56: section number of extra 0 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra1_index; /**< 60: section number of extra 1 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra2_index; /**< 64: section number of extra 2 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra3_index; /**< 68: section number of extra 3 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra4_index; /**< 72: section number of extra 4 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *extra5_index; /**< 76: section number of extra 5 meta index. MOBI_NOTSET if index is not available. */
|
||||
uint32_t *non_text_index; /**< 80: first record number (starting with 0) that's not the book's text */
|
||||
uint32_t *full_name_offset; /**< 84: offset in record 0 (not from start of file) of the full name of the book */
|
||||
uint32_t *full_name_length; /**< 88: length of the full name */
|
||||
uint32_t *locale; /**< 92: first byte is main language: 09 = English, next byte is dialect, 08 = British, 04 = US */
|
||||
uint32_t *dict_input_lang; /**< 96: input language for a dictionary */
|
||||
uint32_t *dict_output_lang; /**< 100: output language for a dictionary */
|
||||
uint32_t *min_version; /**< 104: minimum mobipocket version support needed to read this file. */
|
||||
uint32_t *image_index; /**< 108: first record number (starting with 0) that contains an image (sequential) */
|
||||
uint32_t *huff_rec_index; /**< 112: first huffman compression record */
|
||||
uint32_t *huff_rec_count; /**< 116: huffman compression records count */
|
||||
uint32_t *datp_rec_index; /**< 120: section number of DATP record */
|
||||
uint32_t *datp_rec_count; /**< 124: DATP records count */
|
||||
uint32_t *exth_flags; /**< 128: bitfield. if bit 6 (0x40) is set, then there's an EXTH record */
|
||||
/* 32 unknown bytes, usually 0, related to encryption and unknown6 */
|
||||
/* unknown2 */
|
||||
/* unknown3 */
|
||||
/* unknown4 */
|
||||
/* unknown5 */
|
||||
uint32_t *unknown6; /**< 164: use MOBI_NOTSET , related to encryption*/
|
||||
uint32_t *drm_offset; /**< 168: offset to DRM key info in DRMed files. MOBI_NOTSET if no DRM */
|
||||
uint32_t *drm_count; /**< 172: number of entries in DRM info */
|
||||
uint32_t *drm_size; /**< 176: number of bytes in DRM info */
|
||||
uint32_t *drm_flags; /**< 180: some flags concerning DRM info, bit 0 set if password encryption */
|
||||
/* 8 unknown bytes 0? */
|
||||
/* unknown7 */
|
||||
/* unknown8 */
|
||||
uint16_t *first_text_index; /**< 192: section number of first text record */
|
||||
uint16_t *last_text_index; /**< 194: */
|
||||
uint32_t *fdst_index; /**< 192 (KF8) section number of FDST record */
|
||||
//uint32_t *unknown9; /**< 196: */
|
||||
uint32_t *fdst_section_count; /**< 196 (KF8) */
|
||||
uint32_t *fcis_index; /**< 200: section number of FCIS record */
|
||||
uint32_t *fcis_count; /**< 204: FCIS records count */
|
||||
uint32_t *flis_index; /**< 208: section number of FLIS record */
|
||||
uint32_t *flis_count; /**< 212: FLIS records count */
|
||||
uint32_t *unknown10; /**< 216: */
|
||||
uint32_t *unknown11; /**< 220: */
|
||||
uint32_t *srcs_index; /**< 224: section number of SRCS record */
|
||||
uint32_t *srcs_count; /**< 228: SRCS records count */
|
||||
uint32_t *unknown12; /**< 232: */
|
||||
uint32_t *unknown13; /**< 236: */
|
||||
/* uint16_t fill 0 */
|
||||
uint16_t *extra_flags; /**< 242: extra flags */
|
||||
uint32_t *ncx_index; /**< 244: section number of NCX record */
|
||||
uint32_t *unknown14; /**< 248: */
|
||||
uint32_t *fragment_index; /**< 248 (KF8) section number of fragments record */
|
||||
uint32_t *unknown15; /**< 252: */
|
||||
uint32_t *skeleton_index; /**< 252 (KF8) section number of SKEL record */
|
||||
uint32_t *datp_index; /**< 256: section number of DATP record */
|
||||
uint32_t *unknown16; /**< 260: */
|
||||
uint32_t *guide_index; /**< 260 (KF8) section number of guide record */
|
||||
uint32_t *unknown17; /**< 264: */
|
||||
uint32_t *unknown18; /**< 268: */
|
||||
uint32_t *unknown19; /**< 272: */
|
||||
uint32_t *unknown20; /**< 276: */
|
||||
char *full_name; /**< variable offset (full_name_offset): full name */
|
||||
} MOBIMobiHeader;
|
||||
|
||||
/**
|
||||
@brief Main structure holding all metadata and unparsed records data
|
||||
|
||||
In case of hybrid KF7/KF8 file there are two Records 0.
|
||||
In such case MOBIData is a circular linked list of two independent records, one structure per each Record 0 header.
|
||||
Records data (MOBIPdbRecord structure) is not duplicated in such case - each struct holds same pointers to all records data.
|
||||
*/
|
||||
typedef struct MOBIData {
|
||||
bool use_kf8; /**< Flag: if set to true (default), KF8 part of hybrid file is parsed, if false - KF7 part will be parsed */
|
||||
uint32_t kf8_boundary_offset; /**< Set to KF8 boundary rec number if present, otherwise: MOBI_NOTSET */
|
||||
unsigned char *drm_key; /**< @deprecated Will be removed in future versions */
|
||||
MOBIPdbHeader *ph; /**< Palmdoc database header structure or NULL if not loaded */
|
||||
MOBIRecord0Header *rh; /**< Record0 header structure or NULL if not loaded */
|
||||
MOBIMobiHeader *mh; /**< MOBI header structure or NULL if not loaded */
|
||||
MOBIExthHeader *eh; /**< Linked list of EXTH records or NULL if not loaded */
|
||||
MOBIPdbRecord *rec; /**< Linked list of palmdoc database records or NULL if not loaded */
|
||||
struct MOBIData *next; /**< Pointer to the other part of hybrid file or NULL if not a hybrid file */
|
||||
void *internals; /**< Used internally*/
|
||||
} MOBIData;
|
||||
|
||||
/** @} */ // end of raw_structs group
|
||||
|
||||
/**
|
||||
@defgroup parsed_structs Exported structures for the parsed records metadata and data
|
||||
@{
|
||||
*/
|
||||
|
||||
/**
|
||||
@brief Parsed FDST record
|
||||
|
||||
FDST record contains offsets of main sections in RAWML - raw text data.
|
||||
The sections are usually html part, css parts, svg part.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t fdst_section_count; /**< Number of main sections */
|
||||
uint32_t *fdst_section_starts; /**< Array of section start offsets */
|
||||
uint32_t *fdst_section_ends; /**< Array of section end offsets */
|
||||
} MOBIFdst;
|
||||
|
||||
/**
|
||||
@brief Parsed tag for an index entry
|
||||
*/
|
||||
typedef struct {
|
||||
size_t tagid; /**< Tag id */
|
||||
size_t tagvalues_count; /**< Number of tag values */
|
||||
uint32_t *tagvalues; /**< Array of tag values */
|
||||
} MOBIIndexTag;
|
||||
|
||||
/**
|
||||
@brief Parsed INDX index entry
|
||||
*/
|
||||
typedef struct {
|
||||
char *label; /**< Entry string, zero terminated */
|
||||
size_t tags_count; /**< Number of tags */
|
||||
MOBIIndexTag *tags; /**< Array of tags */
|
||||
} MOBIIndexEntry;
|
||||
|
||||
/**
|
||||
@brief Parsed INDX record
|
||||
*/
|
||||
typedef struct {
|
||||
size_t type; /**< Index type: 0 - normal, 2 - inflection */
|
||||
size_t entries_count; /**< Index entries count */
|
||||
MOBIEncoding encoding; /**< Index encoding */
|
||||
size_t total_entries_count; /**< Total index entries count */
|
||||
size_t ordt_offset; /**< ORDT offset */
|
||||
size_t ligt_offset; /**< LIGT offset */
|
||||
size_t ligt_entries_count; /**< LIGT index entries count */
|
||||
size_t cncx_records_count; /**< Number of compiled NCX records */
|
||||
MOBIPdbRecord *cncx_record; /**< Link to CNCX record */
|
||||
MOBIIndexEntry *entries; /**< Index entries array */
|
||||
char *orth_index_name; /**< Orth index name */
|
||||
} MOBIIndx;
|
||||
|
||||
/**
|
||||
@brief Reconstructed source file.
|
||||
|
||||
All file parts are organized in a linked list.
|
||||
*/
|
||||
typedef struct MOBIPart {
|
||||
size_t uid; /**< Unique id */
|
||||
MOBIFiletype type; /**< File type */
|
||||
size_t size; /**< File size */
|
||||
unsigned char *data; /**< File data */
|
||||
struct MOBIPart *next; /**< Pointer to next part or NULL */
|
||||
} MOBIPart;
|
||||
|
||||
/**
|
||||
@brief Main structure containing reconstructed source parts and indices
|
||||
*/
|
||||
typedef struct {
|
||||
size_t version; /**< Version of Mobipocket document */
|
||||
MOBIFdst *fdst; /**< Parsed FDST record or NULL if not present */
|
||||
MOBIIndx *skel; /**< Parsed skeleton index or NULL if not present */
|
||||
MOBIIndx *frag; /**< Parsed fragments index or NULL if not present */
|
||||
MOBIIndx *guide; /**< Parsed guide index or NULL if not present */
|
||||
MOBIIndx *ncx; /**< Parsed NCX index or NULL if not present */
|
||||
MOBIIndx *orth; /**< Parsed orth index or NULL if not present */
|
||||
MOBIIndx *infl; /**< Parsed infl index or NULL if not present */
|
||||
MOBIPart *flow; /**< Linked list of reconstructed main flow parts or NULL if not present */
|
||||
MOBIPart *markup; /**< Linked list of reconstructed markup files or NULL if not present */
|
||||
MOBIPart *resources; /**< Linked list of reconstructed resources files or NULL if not present */
|
||||
} MOBIRawml;
|
||||
|
||||
/** @} */ // end of parsed_structs group
|
||||
|
||||
/**
|
||||
@defgroup mobi_export Functions exported by the library
|
||||
@{
|
||||
*/
|
||||
MOBI_EXPORT const char * mobi_version(void);
|
||||
MOBI_EXPORT MOBI_RET mobi_load_file(MOBIData *m, FILE *file);
|
||||
MOBI_EXPORT MOBI_RET mobi_load_filename(MOBIData *m, const char *path);
|
||||
|
||||
MOBI_EXPORT MOBIData * mobi_init(void);
|
||||
MOBI_EXPORT void mobi_free(MOBIData *m);
|
||||
|
||||
MOBI_EXPORT MOBI_RET mobi_parse_kf7(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_parse_kf8(MOBIData *m);
|
||||
|
||||
MOBI_EXPORT MOBI_RET mobi_parse_rawml(MOBIRawml *rawml, const MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_parse_rawml_opt(MOBIRawml *rawml, const MOBIData *m, bool parse_toc, bool parse_dict, bool reconstruct);
|
||||
|
||||
MOBI_EXPORT MOBI_RET mobi_get_rawml(const MOBIData *m, char *text, size_t *len);
|
||||
MOBI_EXPORT MOBI_RET mobi_dump_rawml(const MOBIData *m, FILE *file);
|
||||
MOBI_EXPORT MOBI_RET mobi_decode_font_resource(unsigned char **decoded_font, size_t *decoded_size, MOBIPart *part);
|
||||
MOBI_EXPORT MOBI_RET mobi_decode_audio_resource(unsigned char **decoded_resource, size_t *decoded_size, MOBIPart *part);
|
||||
MOBI_EXPORT MOBI_RET mobi_decode_video_resource(unsigned char **decoded_resource, size_t *decoded_size, MOBIPart *part);
|
||||
MOBI_EXPORT MOBI_RET mobi_get_embedded_source(unsigned char **data, size_t *size, const MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_get_embedded_log(unsigned char **data, size_t *size, const MOBIData *m);
|
||||
|
||||
MOBI_EXPORT MOBIPdbRecord * mobi_get_record_by_uid(const MOBIData *m, const size_t uid);
|
||||
MOBI_EXPORT MOBIPdbRecord * mobi_get_record_by_seqnumber(const MOBIData *m, const size_t uid);
|
||||
MOBI_EXPORT MOBIPart * mobi_get_flow_by_uid(const MOBIRawml *rawml, const size_t uid);
|
||||
MOBI_EXPORT MOBIPart * mobi_get_flow_by_fid(const MOBIRawml *rawml, const char *fid);
|
||||
MOBI_EXPORT MOBIPart * mobi_get_resource_by_uid(const MOBIRawml *rawml, const size_t uid);
|
||||
MOBI_EXPORT MOBIPart * mobi_get_resource_by_fid(const MOBIRawml *rawml, const char *fid);
|
||||
MOBI_EXPORT MOBIPart * mobi_get_part_by_uid(const MOBIRawml *rawml, const size_t uid);
|
||||
MOBI_EXPORT MOBI_RET mobi_get_fullname(const MOBIData *m, char *fullname, const size_t len);
|
||||
MOBI_EXPORT size_t mobi_get_first_resource_record(const MOBIData *m);
|
||||
MOBI_EXPORT size_t mobi_get_text_maxsize(const MOBIData *m);
|
||||
MOBI_EXPORT uint16_t mobi_get_textrecord_maxsize(const MOBIData *m);
|
||||
MOBI_EXPORT size_t mobi_get_kf8offset(const MOBIData *m);
|
||||
MOBI_EXPORT size_t mobi_get_kf8boundary_seqnumber(const MOBIData *m);
|
||||
MOBI_EXPORT size_t mobi_get_record_extrasize(const MOBIPdbRecord *record, const uint16_t flags);
|
||||
MOBI_EXPORT size_t mobi_get_record_mb_extrasize(const MOBIPdbRecord *record, const uint16_t flags);
|
||||
MOBI_EXPORT size_t mobi_get_fileversion(const MOBIData *m);
|
||||
MOBI_EXPORT size_t mobi_get_fdst_record_number(const MOBIData *m);
|
||||
MOBI_EXPORT MOBIExthHeader * mobi_get_exthrecord_by_tag(const MOBIData *m, const MOBIExthTag tag);
|
||||
MOBI_EXPORT MOBIExthHeader * mobi_next_exthrecord_by_tag(const MOBIData *m, const MOBIExthTag tag, MOBIExthHeader **start);
|
||||
MOBI_EXPORT MOBI_RET mobi_delete_exthrecord_by_tag(MOBIData *m, const MOBIExthTag tag);
|
||||
MOBI_EXPORT MOBI_RET mobi_add_exthrecord(MOBIData *m, const MOBIExthTag tag, const uint32_t size, const void *value);
|
||||
MOBI_EXPORT MOBIExthMeta mobi_get_exthtagmeta_by_tag(const MOBIExthTag tag);
|
||||
MOBI_EXPORT MOBIFileMeta mobi_get_filemeta_by_type(const MOBIFiletype type);
|
||||
MOBI_EXPORT uint32_t mobi_decode_exthvalue(const unsigned char *data, const size_t size);
|
||||
MOBI_EXPORT char * mobi_decode_exthstring(const MOBIData *m, const unsigned char *data, const size_t size);
|
||||
MOBI_EXPORT struct tm * mobi_pdbtime_to_time(const long pdb_time);
|
||||
MOBI_EXPORT const char * mobi_get_locale_string(const uint32_t locale);
|
||||
MOBI_EXPORT size_t mobi_get_locale_number(const char *locale_string);
|
||||
MOBI_EXPORT uint32_t mobi_get_orth_entry_offset(const MOBIIndexEntry *entry);
|
||||
MOBI_EXPORT uint32_t mobi_get_orth_entry_length(const MOBIIndexEntry *entry);
|
||||
MOBI_EXPORT MOBI_RET mobi_remove_hybrid_part(MOBIData *m, const bool remove_kf8);
|
||||
|
||||
MOBI_EXPORT bool mobi_exists_mobiheader(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_fdst(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_skel_indx(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_frag_indx(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_guide_indx(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_ncx(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_orth(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_exists_infl(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_hybrid(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_encrypted(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_mobipocket(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_dictionary(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_kf8(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_replica(const MOBIData *m);
|
||||
MOBI_EXPORT bool mobi_is_rawml_kf8(const MOBIRawml *rawml);
|
||||
MOBI_EXPORT MOBIRawml * mobi_init_rawml(const MOBIData *m);
|
||||
MOBI_EXPORT void mobi_free_rawml(MOBIRawml *rawml);
|
||||
|
||||
MOBI_EXPORT char * mobi_meta_get_title(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_author(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_publisher(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_imprint(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_description(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_isbn(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_subject(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_publishdate(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_review(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_contributor(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_copyright(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_asin(const MOBIData *m);
|
||||
MOBI_EXPORT char * mobi_meta_get_language(const MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_title(MOBIData *m, const char *title);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_title(MOBIData *m, const char *title);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_title(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_author(MOBIData *m, const char *author);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_author(MOBIData *m, const char *author);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_author(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_publisher(MOBIData *m, const char *publisher);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_publisher(MOBIData *m, const char *publisher);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_publisher(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_imprint(MOBIData *m, const char *imprint);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_imprint(MOBIData *m, const char *imprint);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_imprint(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_description(MOBIData *m, const char *description);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_description(MOBIData *m, const char *description);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_description(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_isbn(MOBIData *m, const char *isbn);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_isbn(MOBIData *m, const char *isbn);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_isbn(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_subject(MOBIData *m, const char *subject);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_subject(MOBIData *m, const char *subject);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_subject(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_publishdate(MOBIData *m, const char *publishdate);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_publishdate(MOBIData *m, const char *publishdate);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_publishdate(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_review(MOBIData *m, const char *review);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_review(MOBIData *m, const char *review);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_review(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_contributor(MOBIData *m, const char *contributor);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_contributor(MOBIData *m, const char *contributor);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_contributor(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_copyright(MOBIData *m, const char *copyright);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_copyright(MOBIData *m, const char *copyright);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_copyright(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_asin(MOBIData *m, const char *asin);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_asin(MOBIData *m, const char *asin);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_asin(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_set_language(MOBIData *m, const char *language);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_add_language(MOBIData *m, const char *language);
|
||||
MOBI_EXPORT MOBI_RET mobi_meta_delete_language(MOBIData *m);
|
||||
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_setkey(MOBIData *m, const char *pid);
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_setkey_serial(MOBIData *m, const char *serial);
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_addvoucher(MOBIData *m, const char *serial, const time_t valid_from, const time_t valid_to,
|
||||
const MOBIExthTag *tamperkeys, const size_t tamperkeys_count);
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_delkey(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_decrypt(MOBIData *m);
|
||||
MOBI_EXPORT MOBI_RET mobi_drm_encrypt(MOBIData *m);
|
||||
|
||||
MOBI_EXPORT MOBI_RET mobi_write_file(FILE *file, MOBIData *m);
|
||||
/** @} */ // end of mobi_export group
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
2059
app/src/main/cpp/libmobi/src/opf.c
vendored
Normal file
2059
app/src/main/cpp/libmobi/src/opf.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
164
app/src/main/cpp/libmobi/src/opf.h
vendored
Normal file
164
app/src/main/cpp/libmobi/src/opf.h
vendored
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/** @file opf.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_opf_h
|
||||
#define libmobi_opf_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
/** @brief Maximum number of opf meta tags */
|
||||
#define OPF_META_MAX_TAGS 256
|
||||
|
||||
/**
|
||||
@defgroup mobi_opf OPF handling structures
|
||||
@{
|
||||
*/
|
||||
|
||||
/** @brief OPF <dc:identifier/> element structure
|
||||
|
||||
At least one identifier must have an id specified,
|
||||
so it can be referenced from the package unique-identifier attribute.
|
||||
*/
|
||||
typedef struct {
|
||||
char *value; /**< element value */
|
||||
char *id; /**< id attribute */
|
||||
char *scheme; /**< opf:scheme (optional) */
|
||||
} OPFidentifier;
|
||||
|
||||
/** @brief OPF <dc:creator/> element structure
|
||||
|
||||
Also applies to <dc:contributor/> element
|
||||
*/
|
||||
typedef struct {
|
||||
char *value; /**< element value */
|
||||
char *file_as; /**< opf:file-as attribute (optional) */
|
||||
char *role; /**< opf:role attribute (optional) */
|
||||
} OPFcreator;
|
||||
|
||||
/** @brief OPF <dc:subject/> element structure */
|
||||
typedef struct {
|
||||
char *value; /**< element value */
|
||||
char *basic_code; /**< BASICCode attribute (optional, non-standard) */
|
||||
} OPFsubject;
|
||||
|
||||
/** @brief OPF <dc:date/> element structure
|
||||
|
||||
Format: YYYY[-MM[-DD]]
|
||||
*/
|
||||
typedef struct {
|
||||
char *value; /**< element value */
|
||||
char *event; /**< opf:event attribute (optional) */
|
||||
} OPFdate;
|
||||
|
||||
/** @brief OPF <dc-metadata/> element structure */
|
||||
typedef struct {
|
||||
OPFcreator **contributor; /**< <dc:contributor/> element (optional) */
|
||||
OPFcreator **creator; /**< <dc:creator/> element (optional) */
|
||||
OPFidentifier **identifier; /**< <dc:identifier/> element (required) */
|
||||
OPFsubject **subject; /**< <dc:subject/> element (optional) */
|
||||
OPFdate **date; /**< <dc:date/> element (optional) */
|
||||
char **description; /**< <dc:description/> element (optional) */
|
||||
char **language; /**< <dc:language/> element (required) */
|
||||
char **publisher; /**< <dc:publisher/> element (optional) */
|
||||
char **rights; /**< <dc:rights/> element (optional) */
|
||||
char **source; /**< <dc:source/> element (optional) */
|
||||
char **title; /**< <dc:title/> element (required) */
|
||||
char **type; /**< <dc:type/> element (optional) */
|
||||
} OPFdcmeta;
|
||||
|
||||
/** @brief OPF <srp/> element structure */
|
||||
typedef struct {
|
||||
char *value; /**< element value */
|
||||
char *currency; /**< currency attribute */
|
||||
} OPFsrp;
|
||||
|
||||
/** @brief OPF <x-metadata/> element structure */
|
||||
typedef struct {
|
||||
OPFsrp **srp; /**< <srp/> element */
|
||||
char **adult; /**< <adult/> element */
|
||||
char **default_lookup_index; /**< <DefaultLookupIndex/> element */
|
||||
char **dict_short_name; /**< <DictionaryVeryShortName/> element */
|
||||
char **dictionary_in_lang; /**< <DictionaryInLanguage/> element */
|
||||
char **dictionary_out_lang; /**< <DictionaryOutLanguage/> element */
|
||||
char **embedded_cover; /**< <EmbeddedCover/> element */
|
||||
char **imprint; /**< <imprint/> element */
|
||||
char **review; /**< <review/> element */
|
||||
} OPFxmeta;
|
||||
|
||||
/** @brief OPF <meta/> element structure */
|
||||
typedef struct {
|
||||
char *name; /**< name attribute (required) */
|
||||
char *content; /**< content attribute (required) */
|
||||
} OPFmeta;
|
||||
|
||||
/** @brief OPF <metadata/> element structure */
|
||||
typedef struct {
|
||||
OPFmeta **meta; /**< <meta/> element (optional) */
|
||||
OPFdcmeta *dc_meta; /**< <dc-metadata/> element */
|
||||
OPFxmeta *x_meta; /**< <x-metadata/> element */
|
||||
} OPFmetadata;
|
||||
|
||||
/** @brief OPF <item/> element structure */
|
||||
typedef struct {
|
||||
char *id; /**< id attribute (required) */
|
||||
char *href; /**< href attribute (required) */
|
||||
char *media_type; /**< media-type attribute (required) */
|
||||
} OPFitem;
|
||||
|
||||
/** @brief OPF <manifest/> element structure */
|
||||
typedef struct {
|
||||
OPFitem **item; /**< <item/> element */
|
||||
} OPFmanifest;
|
||||
|
||||
/** @brief OPF <spine/> element structure */
|
||||
typedef struct {
|
||||
char *toc; /**< toc attribute (required) */
|
||||
char **itemref; /**< <itemref idref="xxx"/> element */
|
||||
} OPFspine;
|
||||
|
||||
/** @brief OPF <reference/> tag structure */
|
||||
typedef struct {
|
||||
char *type; /**< type attribute (required) */
|
||||
char *title; /**< title attribute */
|
||||
char *href; /**< href attribute (required) */
|
||||
} OPFreference;
|
||||
|
||||
/** @brief OPF <guide/> element structure */
|
||||
typedef struct {
|
||||
OPFreference **reference; /**< <reference/> element tag */
|
||||
} OPFguide;
|
||||
|
||||
/** @brief OPF <package/> element structure */
|
||||
typedef struct {
|
||||
//char *uid; /**< <package unique-identifier="uid"/> */
|
||||
OPFmetadata *metadata; /**< <metadata/> (required) */
|
||||
OPFmanifest *manifest; /**< <manifest/> (required) */
|
||||
OPFspine *spine; /**< <spine/> (required) */
|
||||
OPFguide *guide; /**< <guide/> (optional) */
|
||||
} OPF;
|
||||
|
||||
/** @brief NCX index entry structure */
|
||||
typedef struct {
|
||||
size_t id; /**< Sequential id */
|
||||
char *text; /**< Entry text content */
|
||||
char *target; /**< Entry target reference */
|
||||
size_t level; /**< Entry level */
|
||||
size_t parent; /**< Entry parent */
|
||||
size_t first_child; /**< First child id */
|
||||
size_t last_child; /**< Last child id */
|
||||
} NCX;
|
||||
/** @} */
|
||||
|
||||
|
||||
MOBI_RET mobi_build_opf(MOBIRawml *rawml, const MOBIData *m);
|
||||
MOBI_RET mobi_build_ncx(MOBIRawml *rawml, const OPF *opf);
|
||||
|
||||
#endif
|
||||
2199
app/src/main/cpp/libmobi/src/parse_rawml.c
vendored
Normal file
2199
app/src/main/cpp/libmobi/src/parse_rawml.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
42
app/src/main/cpp/libmobi/src/parse_rawml.h
vendored
Normal file
42
app/src/main/cpp/libmobi/src/parse_rawml.h
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/** @file parse_rawml.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_parse_rawml_h
|
||||
#define mobi_parse_rawml_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
#define MOBI_ATTRNAME_MAXSIZE 150 /**< Maximum length of tag attribute name, like "href" */
|
||||
#define MOBI_ATTRVALUE_MAXSIZE 150 /**< Maximum length of tag attribute value */
|
||||
|
||||
/**
|
||||
@brief Result data returned by mobi_search_links_kf7() and mobi_search_links_kf8()
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned char *start; /**< Beginning data to be replaced */
|
||||
unsigned char *end; /**< End of data to be replaced */
|
||||
char value[MOBI_ATTRVALUE_MAXSIZE + 1]; /**< Attribute value */
|
||||
bool is_url; /**< True if value is part of css url attribute */
|
||||
} MOBIResult;
|
||||
|
||||
/**
|
||||
@brief HTML attribute type
|
||||
*/
|
||||
typedef enum {
|
||||
ATTR_ID = 0, /**< Attribute 'id' */
|
||||
ATTR_NAME /**< Attribute 'name' */
|
||||
} MOBIAttrType;
|
||||
|
||||
|
||||
MOBI_RET mobi_get_id_by_posoff(uint32_t *file_number, char *id, const MOBIRawml *rawml, const size_t pos_fid, const size_t pos_off, MOBIAttrType *pref_attr);
|
||||
MOBI_RET mobi_find_attrvalue(MOBIResult *result, const unsigned char *data_start, const unsigned char *data_end, const MOBIFiletype type, const char *needle);
|
||||
|
||||
#endif
|
||||
375
app/src/main/cpp/libmobi/src/randombytes.c
vendored
Normal file
375
app/src/main/cpp/libmobi/src/randombytes.c
vendored
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
/** @file randombytes.c
|
||||
* @brief Portable function for generating random data
|
||||
*
|
||||
* Copyright (c) 2022 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*
|
||||
* This code is based on libsodium's randombytes_buf function
|
||||
* We just extract the single function we need from libsodium and adjust it for our use.
|
||||
* Most of the code originates from:
|
||||
* https://github.com/jedisct1/libsodium/blob/d250858c7445b7de94e912b529b81defe20d4aaa/src/libsodium/randombytes/sysrandom/randombytes_sysrandom.c
|
||||
* Original code uses following license:
|
||||
*
|
||||
* ISC License
|
||||
*
|
||||
* Copyright (c) 2013-2022
|
||||
* Frank Denis <j at pureftpd dot org>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "randombytes.h"
|
||||
#include "debug.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#ifndef _WIN32
|
||||
# include <sys/stat.h>
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
# define _LINUX_SOURCE
|
||||
#endif
|
||||
#ifdef HAVE_SYS_RANDOM_H
|
||||
# include <sys/random.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
# define BLOCK_ON_DEV_RANDOM
|
||||
# include <poll.h>
|
||||
# ifdef HAVE_GETRANDOM
|
||||
# define HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
# else
|
||||
# include <sys/syscall.h>
|
||||
# if defined(SYS_getrandom) && defined(__NR_getrandom)
|
||||
# define getrandom(B, S, F) syscall(SYS_getrandom, (B), (int) (S), (F))
|
||||
# define HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
# endif
|
||||
# endif
|
||||
#elif defined(__FreeBSD__) || defined(__DragonFly__)
|
||||
# include <sys/param.h>
|
||||
# if (defined(__FreeBSD_version) && __FreeBSD_version >= 1200000) || \
|
||||
(defined(__DragonFly_version) && __DragonFly_version >= 500700)
|
||||
# define HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
typedef struct {
|
||||
int random_data_source_fd;
|
||||
int getrandom_available;
|
||||
} MOBIRandom;
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
# include <windows.h>
|
||||
# define RtlGenRandom SystemFunction036
|
||||
# if defined(__cplusplus)
|
||||
extern "C"
|
||||
# endif
|
||||
BOOLEAN NTAPI RtlGenRandom(PVOID RandomBuffer, ULONG RandomBufferLength);
|
||||
# ifdef _MSC_VER
|
||||
# pragma comment(lib, "advapi32.lib")
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if defined(__OpenBSD__) || defined(__CloudABI__) || defined(__wasi__)
|
||||
# define HAVE_SAFE_ARC4RANDOM
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SAFE_ARC4RANDOM
|
||||
|
||||
/**
|
||||
@brief Read a buffer of random bytes using arc4random_buf call
|
||||
|
||||
@param[in,out] handle Handle
|
||||
@param[in,out] buf Buffer
|
||||
@param[in] size Buffer size
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_sysrandom_buf(MOBIRandom *handle, void *buf, const size_t size) {
|
||||
UNUSED(handle);
|
||||
arc4random_buf(buf, size);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
#else /* HAVE_SAFE_ARC4RANDOM */
|
||||
|
||||
# ifndef _WIN32
|
||||
/**
|
||||
@brief Read a buffer of random bytes from file descriptoir
|
||||
|
||||
@param[in] fd File descriptior
|
||||
@param[in,out] buf_ Buffer
|
||||
@param[in] size Buffer size
|
||||
@return Number of bytes read
|
||||
*/
|
||||
static ssize_t mobi_safe_read(const int fd, void *buf_, size_t size) {
|
||||
unsigned char *buf = (unsigned char *) buf_;
|
||||
ssize_t readnb;
|
||||
|
||||
do {
|
||||
while ((readnb = read(fd, buf, size)) < (ssize_t) 0 && (errno == EINTR || errno == EAGAIN));
|
||||
if (readnb < (ssize_t) 0) {
|
||||
return readnb;
|
||||
}
|
||||
if (readnb == (ssize_t) 0) {
|
||||
break;
|
||||
}
|
||||
size -= (size_t) readnb;
|
||||
buf += readnb;
|
||||
} while (size > (ssize_t) 0);
|
||||
|
||||
return (ssize_t) (buf - (unsigned char *) buf_);
|
||||
}
|
||||
|
||||
# ifdef BLOCK_ON_DEV_RANDOM
|
||||
/**
|
||||
@brief Block on /dev/random until enough entropy is available
|
||||
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_block_on_dev_random(void) {
|
||||
int fd = open("/dev/random", O_RDONLY);
|
||||
if (fd == -1) {
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
struct pollfd pfd;
|
||||
pfd.fd = fd;
|
||||
pfd.events = POLLIN;
|
||||
pfd.revents = 0;
|
||||
int pret;
|
||||
do {
|
||||
pret = poll(&pfd, 1, -1);
|
||||
} while (pret < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
if (pret != 1) {
|
||||
(void) close(fd);
|
||||
errno = EIO;
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
if (close(fd) != 0) {
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
# endif /* BLOCK_ON_DEV_RANDOM */
|
||||
|
||||
/**
|
||||
@brief Open random device, wait for enough entropy if supported
|
||||
|
||||
@return Random device file descriptor
|
||||
*/
|
||||
static int mobi_randombytes_sysrandom_random_dev_open(void) {
|
||||
|
||||
# ifdef BLOCK_ON_DEV_RANDOM
|
||||
if (mobi_randombytes_block_on_dev_random() != MOBI_SUCCESS) {
|
||||
return -1;
|
||||
}
|
||||
# endif
|
||||
|
||||
static const char *devices[] = {
|
||||
"/dev/urandom",
|
||||
"/dev/random", NULL
|
||||
};
|
||||
const char **device = devices;
|
||||
|
||||
do {
|
||||
int fd = open(*device, O_RDONLY);
|
||||
if (fd != -1) {
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0 &&
|
||||
# ifdef S_ISNAM
|
||||
(S_ISNAM(st.st_mode) || S_ISCHR(st.st_mode))
|
||||
# else
|
||||
S_ISCHR(st.st_mode)
|
||||
# endif
|
||||
) {
|
||||
# if defined(F_SETFD) && defined(FD_CLOEXEC)
|
||||
(void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
|
||||
# endif
|
||||
return fd;
|
||||
}
|
||||
(void) close(fd);
|
||||
} else if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
device++;
|
||||
} while (*device != NULL);
|
||||
|
||||
errno = EIO;
|
||||
return -1;
|
||||
}
|
||||
|
||||
# ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
/**
|
||||
@brief Read a buffer of random bytes using getrandom system call
|
||||
In libmobi we only need small KEYSIZE buffer, so we don't have to handle buffers over 256 bytes and read chunks
|
||||
|
||||
@param[in,out] buf Buffer
|
||||
@param[in] size Buffer size (up to 256 bytes)
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_linux_getrandom(void *buf, const size_t size) {
|
||||
|
||||
if (size > 256U) {
|
||||
debug_print("This function can only handle buffer size up to 256 bytes (%zu requested)\n", size);
|
||||
return MOBI_PARAM_ERR;
|
||||
}
|
||||
int readnb;
|
||||
do {
|
||||
readnb = getrandom(buf, size, 0);
|
||||
} while (readnb < 0 && (errno == EINTR || errno == EAGAIN));
|
||||
|
||||
if (readnb != (int) size) {
|
||||
debug_print("Getrandom failed (%s)\n", strerror(errno));
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
# endif /* HAVE_LINUX_COMPATIBLE_GETRANDOM */
|
||||
|
||||
/**
|
||||
@brief Initialize random data source
|
||||
|
||||
@param[in,out] handle Handle
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_sysrandom_init(MOBIRandom *handle) {
|
||||
# define NEEDS_INIT
|
||||
const int errno_save = errno;
|
||||
|
||||
# ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
{
|
||||
unsigned char fodder[16];
|
||||
|
||||
if (mobi_randombytes_linux_getrandom(fodder, sizeof fodder) == MOBI_SUCCESS) {
|
||||
handle->getrandom_available = 1;
|
||||
errno = errno_save;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
handle->getrandom_available = 0;
|
||||
}
|
||||
# endif
|
||||
|
||||
if ((handle->random_data_source_fd = mobi_randombytes_sysrandom_random_dev_open()) == -1) {
|
||||
debug_print("Couldn't open random device (%s)\n", strerror(errno));
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
errno = errno_save;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Initialize random data source
|
||||
|
||||
@param[in,out] handle Handle
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_sysrandom_close(MOBIRandom *handle) {
|
||||
# define NEEDS_CLOSE
|
||||
MOBI_RET ret = MOBI_DRM_RANDOM_ERR;
|
||||
if (handle->random_data_source_fd != -1 && close(handle->random_data_source_fd) == 0) {
|
||||
handle->random_data_source_fd = -1;
|
||||
ret = MOBI_SUCCESS;
|
||||
}
|
||||
# ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
if (handle->getrandom_available != 0) {
|
||||
ret = MOBI_SUCCESS;
|
||||
}
|
||||
# endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
# endif /* _WIN32 */
|
||||
|
||||
/**
|
||||
@brief Read a buffer of random bytes
|
||||
|
||||
@param[in,out] handle Handle
|
||||
@param[in,out] buf Buffer
|
||||
@param[in] size Buffer size (up to 256 bytes)
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_randombytes_sysrandom_buf(MOBIRandom *handle, void *buf, const size_t size) {
|
||||
# ifndef _WIN32
|
||||
# ifdef HAVE_LINUX_COMPATIBLE_GETRANDOM
|
||||
if (handle->getrandom_available != 0) {
|
||||
return mobi_randombytes_linux_getrandom(buf, size);
|
||||
}
|
||||
# endif
|
||||
if (handle->random_data_source_fd == -1 ||
|
||||
mobi_safe_read(handle->random_data_source_fd, buf, size) != (ssize_t) size) {
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
# else /* _WIN32 */
|
||||
UNUSED(handle);
|
||||
if (! RtlGenRandom((PVOID) buf, (ULONG) size)) {
|
||||
return MOBI_DRM_RANDOM_ERR;
|
||||
}
|
||||
# endif /* _WIN32 */
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
#endif /* HAVE_SAFE_ARC4RANDOM */
|
||||
|
||||
/**
|
||||
@brief Fill buffer with random bytes
|
||||
|
||||
@param[in,out] buf Buffer
|
||||
@param[in] size Buffer size (up to 256 bytes)
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_randombytes(void *buf, const size_t size) {
|
||||
|
||||
MOBIRandom handle = {
|
||||
.random_data_source_fd = -1,
|
||||
.getrandom_available = 0
|
||||
};
|
||||
|
||||
MOBI_RET ret;
|
||||
#ifdef NEEDS_INIT
|
||||
ret = mobi_randombytes_sysrandom_init(&handle);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (size > (size_t) 0U) {
|
||||
ret = mobi_randombytes_sysrandom_buf(&handle, buf, size);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
debug_print("%s\n", "Generating random data failed");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#ifdef NEEDS_CLOSE
|
||||
if (mobi_randombytes_sysrandom_close(&handle) != MOBI_SUCCESS) {
|
||||
debug_print("%s\n", "Closing random data source failed");
|
||||
}
|
||||
#endif
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
25
app/src/main/cpp/libmobi/src/randombytes.h
vendored
Normal file
25
app/src/main/cpp/libmobi/src/randombytes.h
vendored
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/** @file randombytes.h
|
||||
*
|
||||
* Copyright (c) 2021 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_randombytes_h
|
||||
#define libmobi_randombytes_h
|
||||
|
||||
#include "mobi.h"
|
||||
|
||||
/**
|
||||
@brief Write n random bytes of high quality to buf
|
||||
|
||||
@param[in,out] buf Buffer to be filled with random bytes
|
||||
@param[in] len Buffer length
|
||||
@return On success returns MOBI_SUCCESS
|
||||
*/
|
||||
MOBI_RET mobi_randombytes(void *buf, const size_t len);
|
||||
|
||||
#endif
|
||||
924
app/src/main/cpp/libmobi/src/read.c
vendored
Normal file
924
app/src/main/cpp/libmobi/src/read.c
vendored
Normal file
|
|
@ -0,0 +1,924 @@
|
|||
/** @file read.c
|
||||
* @brief Functions for reading and parsing of MOBI document
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "read.h"
|
||||
#include "util.h"
|
||||
#include "index.h"
|
||||
#include "debug.h"
|
||||
|
||||
/**
|
||||
@brief Read palm database header from file into MOBIData structure (MOBIPdbHeader)
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with read data
|
||||
@param[in] file Filedescriptor to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_pdbheader(MOBIData *m, FILE *file) {
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
if (!file) {
|
||||
return MOBI_FILE_NOT_FOUND;
|
||||
}
|
||||
MOBIBuffer *buf = mobi_buffer_init(PALMDB_HEADER_LEN);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
const size_t len = fread(buf->data, 1, PALMDB_HEADER_LEN, file);
|
||||
if (len != PALMDB_HEADER_LEN) {
|
||||
mobi_buffer_free(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
m->ph = calloc(1, sizeof(MOBIPdbHeader));
|
||||
if (m->ph == NULL) {
|
||||
debug_print("%s", "Memory allocation for pdb header failed\n");
|
||||
mobi_buffer_free(buf);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
/* parse header */
|
||||
mobi_buffer_getstring(m->ph->name, buf, PALMDB_NAME_SIZE_MAX);
|
||||
m->ph->attributes = mobi_buffer_get16(buf);
|
||||
m->ph->version = mobi_buffer_get16(buf);
|
||||
m->ph->ctime = mobi_buffer_get32(buf);
|
||||
m->ph->mtime = mobi_buffer_get32(buf);
|
||||
m->ph->btime = mobi_buffer_get32(buf);
|
||||
m->ph->mod_num = mobi_buffer_get32(buf);
|
||||
m->ph->appinfo_offset = mobi_buffer_get32(buf);
|
||||
m->ph->sortinfo_offset = mobi_buffer_get32(buf);
|
||||
mobi_buffer_getstring(m->ph->type, buf, 4);
|
||||
mobi_buffer_getstring(m->ph->creator, buf, 4);
|
||||
m->ph->uid = mobi_buffer_get32(buf);
|
||||
m->ph->next_rec = mobi_buffer_get32(buf);
|
||||
m->ph->rec_count = mobi_buffer_get16(buf);
|
||||
mobi_buffer_free(buf);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read list of database records from file into MOBIData structure (MOBIPdbRecord)
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with read data
|
||||
@param[in] file Filedescriptor to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_reclist(MOBIData *m, FILE *file) {
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
if (!file) {
|
||||
debug_print("%s", "File not ready\n");
|
||||
return MOBI_FILE_NOT_FOUND;
|
||||
}
|
||||
m->rec = calloc(1, sizeof(MOBIPdbRecord));
|
||||
if (m->rec == NULL) {
|
||||
debug_print("%s", "Memory allocation for pdb record failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
MOBIPdbRecord *curr = m->rec;
|
||||
for (int i = 0; i < m->ph->rec_count; i++) {
|
||||
MOBIBuffer *buf = mobi_buffer_init(PALMDB_RECORD_INFO_SIZE);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
const size_t len = fread(buf->data, 1, PALMDB_RECORD_INFO_SIZE, file);
|
||||
if (len != PALMDB_RECORD_INFO_SIZE) {
|
||||
mobi_buffer_free(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if (i > 0) {
|
||||
curr->next = calloc(1, sizeof(MOBIPdbRecord));
|
||||
if (curr->next == NULL) {
|
||||
debug_print("%s", "Memory allocation for pdb record failed\n");
|
||||
mobi_buffer_free(buf);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
curr->offset = mobi_buffer_get32(buf);
|
||||
curr->attributes = mobi_buffer_get8(buf);
|
||||
const uint8_t h = mobi_buffer_get8(buf);
|
||||
const uint16_t l = mobi_buffer_get16(buf);
|
||||
curr->uid = (uint32_t) h << 16 | l;
|
||||
curr->next = NULL;
|
||||
mobi_buffer_free(buf);
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read record data and size from file into MOBIData structure (MOBIPdbRecord)
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with read data
|
||||
@param[in] file Filedescriptor to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_rec(MOBIData *m, FILE *file) {
|
||||
MOBI_RET ret;
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
MOBIPdbRecord *curr = m->rec;
|
||||
while (curr != NULL) {
|
||||
MOBIPdbRecord *next;
|
||||
size_t size;
|
||||
if (curr->next != NULL) {
|
||||
next = curr->next;
|
||||
size = next->offset - curr->offset;
|
||||
} else {
|
||||
fseek(file, 0, SEEK_END);
|
||||
long diff = ftell(file) - curr->offset;
|
||||
if (diff <= 0) {
|
||||
debug_print("Wrong record size: %li\n", diff);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
size = (size_t) diff;
|
||||
next = NULL;
|
||||
}
|
||||
|
||||
curr->size = size;
|
||||
ret = mobi_load_recdata(curr, file);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
debug_print("Error loading record uid %i data\n", curr->uid);
|
||||
mobi_free_rec(m);
|
||||
return ret;
|
||||
}
|
||||
curr = next;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read record data from file into MOBIPdbRecord structure
|
||||
|
||||
@param[in,out] rec MOBIPdbRecord structure to be filled with read data
|
||||
@param[in] file Filedescriptor to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_recdata(MOBIPdbRecord *rec, FILE *file) {
|
||||
const int ret = fseek(file, rec->offset, SEEK_SET);
|
||||
if (ret != 0) {
|
||||
debug_print("Record %i not found\n", rec->uid);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
rec->data = malloc(rec->size);
|
||||
if (rec->data == NULL) {
|
||||
debug_print("%s", "Memory allocation for pdb record data failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
const size_t len = fread(rec->data, 1, rec->size, file);
|
||||
if (len < rec->size) {
|
||||
debug_print("Truncated data in record %i\n", rec->uid);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse EXTH header from Record 0 into MOBIData structure (MOBIExthHeader)
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with parsed data
|
||||
@param[in] buf MOBIBuffer buffer to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_extheader(MOBIData *m, MOBIBuffer *buf) {
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
char exth_magic[5];
|
||||
const size_t header_length = 12;
|
||||
mobi_buffer_getstring(exth_magic, buf, 4);
|
||||
const size_t exth_length = mobi_buffer_get32(buf) - header_length;
|
||||
const size_t rec_count = mobi_buffer_get32(buf);
|
||||
if (strncmp(exth_magic, EXTH_MAGIC, 4) != 0 ||
|
||||
exth_length + buf->offset > buf->maxlen ||
|
||||
rec_count == 0 || rec_count > MOBI_EXTH_MAXCNT) {
|
||||
debug_print("%s", "Sanity checks for EXTH header failed\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const size_t saved_maxlen = buf->maxlen;
|
||||
buf->maxlen = exth_length + buf->offset;
|
||||
m->eh = calloc(1, sizeof(MOBIExthHeader));
|
||||
if (m->eh == NULL) {
|
||||
debug_print("%s", "Memory allocation for EXTH header failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
MOBIExthHeader *curr = m->eh;
|
||||
for (size_t i = 0; i < rec_count; i++) {
|
||||
if (curr->data) {
|
||||
curr->next = calloc(1, sizeof(MOBIExthHeader));
|
||||
if (curr->next == NULL) {
|
||||
debug_print("%s", "Memory allocation for EXTH header failed\n");
|
||||
mobi_free_eh(m);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
curr->tag = mobi_buffer_get32(buf);
|
||||
/* data size = record size minus 8 bytes for uid and size */
|
||||
curr->size = mobi_buffer_get32(buf) - 8;
|
||||
if (curr->size == 0) {
|
||||
debug_print("Skip record %i, data too short\n", curr->tag);
|
||||
continue;
|
||||
}
|
||||
if (buf->offset + curr->size > buf->maxlen) {
|
||||
debug_print("Record %i too long\n", curr->tag);
|
||||
mobi_free_eh(m);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
curr->data = malloc(curr->size);
|
||||
if (curr->data == NULL) {
|
||||
debug_print("Memory allocation for EXTH record %i failed\n", curr->tag);
|
||||
mobi_free_eh(m);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
mobi_buffer_getraw(curr->data, buf, curr->size);
|
||||
curr->next = NULL;
|
||||
}
|
||||
buf->maxlen = saved_maxlen;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse MOBI header from Record 0 into MOBIData structure (MOBIMobiHeader)
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with parsed data
|
||||
@param[in] buf MOBIBuffer buffer to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_mobiheader(MOBIData *m, MOBIBuffer *buf) {
|
||||
int isKF8 = 0;
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
m->mh = calloc(1, sizeof(MOBIMobiHeader));
|
||||
if (m->mh == NULL) {
|
||||
debug_print("%s", "Memory allocation for MOBI header failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
mobi_buffer_getstring(m->mh->mobi_magic, buf, 4);
|
||||
mobi_buffer_dup32(&m->mh->header_length, buf);
|
||||
if (strcmp(m->mh->mobi_magic, MOBI_MAGIC) != 0 || m->mh->header_length == NULL) {
|
||||
debug_print("%s", "MOBI header not found\n");
|
||||
mobi_free_mh(m->mh);
|
||||
m->mh = NULL;
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const size_t saved_maxlen = buf->maxlen;
|
||||
/* some old files declare zero length mobi header, try to read first 24 bytes anyway */
|
||||
uint32_t header_length = (*m->mh->header_length > 0) ? *m->mh->header_length : 24;
|
||||
/* read only declared MOBI header length (curr offset minus 8 already read bytes) */
|
||||
const size_t left_length = header_length + buf->offset - 8;
|
||||
buf->maxlen = saved_maxlen < left_length ? saved_maxlen : left_length;
|
||||
mobi_buffer_dup32(&m->mh->mobi_type, buf);
|
||||
uint32_t encoding = mobi_buffer_get32(buf);
|
||||
if (encoding == 1252) {
|
||||
m->mh->text_encoding = malloc(sizeof(MOBIEncoding));
|
||||
if (m->mh->text_encoding == NULL) {
|
||||
debug_print("%s", "Memory allocation for MOBI header failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
*m->mh->text_encoding = MOBI_CP1252;
|
||||
}
|
||||
else if (encoding == 65001) {
|
||||
m->mh->text_encoding = malloc(sizeof(MOBIEncoding));
|
||||
if (m->mh->text_encoding == NULL) {
|
||||
debug_print("%s", "Memory allocation for MOBI header failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
*m->mh->text_encoding = MOBI_UTF8;
|
||||
} else {
|
||||
debug_print("Unknown encoding in mobi header: %i\n", encoding);
|
||||
}
|
||||
mobi_buffer_dup32(&m->mh->uid, buf);
|
||||
mobi_buffer_dup32(&m->mh->version, buf);
|
||||
if (header_length >= MOBI_HEADER_V7_SIZE
|
||||
&& m->mh->version && *m->mh->version == 8) {
|
||||
isKF8 = 1;
|
||||
}
|
||||
mobi_buffer_dup32(&m->mh->orth_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->infl_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->names_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->keys_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra0_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra1_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra2_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra3_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra4_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->extra5_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->non_text_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->full_name_offset, buf);
|
||||
mobi_buffer_dup32(&m->mh->full_name_length, buf);
|
||||
mobi_buffer_dup32(&m->mh->locale, buf);
|
||||
mobi_buffer_dup32(&m->mh->dict_input_lang, buf);
|
||||
mobi_buffer_dup32(&m->mh->dict_output_lang, buf);
|
||||
mobi_buffer_dup32(&m->mh->min_version, buf);
|
||||
mobi_buffer_dup32(&m->mh->image_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->huff_rec_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->huff_rec_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->datp_rec_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->datp_rec_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->exth_flags, buf);
|
||||
mobi_buffer_seek(buf, 32); /* 32 unknown bytes */
|
||||
mobi_buffer_dup32(&m->mh->unknown6, buf);
|
||||
mobi_buffer_dup32(&m->mh->drm_offset, buf);
|
||||
mobi_buffer_dup32(&m->mh->drm_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->drm_size, buf);
|
||||
mobi_buffer_dup32(&m->mh->drm_flags, buf);
|
||||
mobi_buffer_seek(buf, 8); /* 8 unknown bytes */
|
||||
if (isKF8) {
|
||||
mobi_buffer_dup32(&m->mh->fdst_index, buf);
|
||||
} else {
|
||||
mobi_buffer_dup16(&m->mh->first_text_index, buf);
|
||||
mobi_buffer_dup16(&m->mh->last_text_index, buf);
|
||||
}
|
||||
mobi_buffer_dup32(&m->mh->fdst_section_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->fcis_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->fcis_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->flis_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->flis_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown10, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown11, buf);
|
||||
mobi_buffer_dup32(&m->mh->srcs_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->srcs_count, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown12, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown13, buf);
|
||||
mobi_buffer_seek(buf, 2); /* 2 byte fill */
|
||||
mobi_buffer_dup16(&m->mh->extra_flags, buf);
|
||||
mobi_buffer_dup32(&m->mh->ncx_index, buf);
|
||||
if (isKF8) {
|
||||
mobi_buffer_dup32(&m->mh->fragment_index, buf);
|
||||
mobi_buffer_dup32(&m->mh->skeleton_index, buf);
|
||||
} else {
|
||||
mobi_buffer_dup32(&m->mh->unknown14, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown15, buf);
|
||||
}
|
||||
mobi_buffer_dup32(&m->mh->datp_index, buf);
|
||||
if (isKF8) {
|
||||
mobi_buffer_dup32(&m->mh->guide_index, buf);
|
||||
} else {
|
||||
mobi_buffer_dup32(&m->mh->unknown16, buf);
|
||||
}
|
||||
mobi_buffer_dup32(&m->mh->unknown17, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown18, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown19, buf);
|
||||
mobi_buffer_dup32(&m->mh->unknown20, buf);
|
||||
if (buf->maxlen > buf->offset) {
|
||||
debug_print("Skipping %zu unknown bytes in MOBI header\n", (buf->maxlen - buf->offset));
|
||||
mobi_buffer_setpos(buf, buf->maxlen);
|
||||
}
|
||||
buf->maxlen = saved_maxlen;
|
||||
/* get full name stored at m->mh->full_name_offset */
|
||||
if (m->mh->full_name_offset && m->mh->full_name_length) {
|
||||
const size_t saved_offset = buf->offset;
|
||||
const uint32_t full_name_length = min(*m->mh->full_name_length, MOBI_TITLE_SIZEMAX);
|
||||
mobi_buffer_setpos(buf, *m->mh->full_name_offset);
|
||||
m->mh->full_name = malloc(full_name_length + 1);
|
||||
if (m->mh->full_name == NULL) {
|
||||
debug_print("%s", "Memory allocation for full name failed\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
if (full_name_length) {
|
||||
mobi_buffer_getstring(m->mh->full_name, buf, full_name_length);
|
||||
} else {
|
||||
m->mh->full_name[0] = '\0';
|
||||
}
|
||||
mobi_buffer_setpos(buf, saved_offset);
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse Record 0 into MOBIData structure
|
||||
|
||||
This function will parse MOBIRecord0Header, MOBIMobiHeader and MOBIExthHeader
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with parsed data
|
||||
@param[in] seqnumber Sequential number of the palm database record
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_record0(MOBIData *m, const size_t seqnumber) {
|
||||
MOBI_RET ret;
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
const MOBIPdbRecord *record0 = mobi_get_record_by_seqnumber(m, seqnumber);
|
||||
if (record0 == NULL) {
|
||||
debug_print("%s", "Record 0 not loaded\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if (record0->size < RECORD0_HEADER_LEN) {
|
||||
debug_print("%s", "Record 0 too short\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(record0->data, record0->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
m->rh = calloc(1, sizeof(MOBIRecord0Header));
|
||||
if (m->rh == NULL) {
|
||||
debug_print("%s", "Memory allocation for record 0 header failed\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
/* parse palmdoc header */
|
||||
const uint16_t compression = mobi_buffer_get16(buf);
|
||||
mobi_buffer_seek(buf, 2); // unused 2 bytes, zeroes
|
||||
if ((compression != MOBI_COMPRESSION_NONE &&
|
||||
compression != MOBI_COMPRESSION_PALMDOC &&
|
||||
compression != MOBI_COMPRESSION_HUFFCDIC)) {
|
||||
debug_print("Wrong record0 header: %c%c%c%c\n", record0->data[0], record0->data[1], record0->data[2], record0->data[3]);
|
||||
mobi_buffer_free_null(buf);
|
||||
free(m->rh);
|
||||
m->rh = NULL;
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
m->rh->compression_type = compression;
|
||||
m->rh->text_length = mobi_buffer_get32(buf);
|
||||
m->rh->text_record_count = mobi_buffer_get16(buf);
|
||||
m->rh->text_record_size = mobi_buffer_get16(buf);
|
||||
m->rh->encryption_type = mobi_buffer_get16(buf);
|
||||
m->rh->unknown1 = mobi_buffer_get16(buf);
|
||||
if (mobi_is_mobipocket(m)) {
|
||||
/* parse mobi header if present */
|
||||
ret = mobi_parse_mobiheader(m, buf);
|
||||
if (ret == MOBI_SUCCESS) {
|
||||
/* parse exth header if present */
|
||||
mobi_parse_extheader(m, buf);
|
||||
}
|
||||
}
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Calculate the size of extra bytes at the end of text record
|
||||
|
||||
@param[in] record MOBIPdbRecord structure containing the record
|
||||
@param[in] flags Flags from MOBI header (extra_flags)
|
||||
@return The size of trailing bytes, MOBI_NOTSET on failure
|
||||
*/
|
||||
size_t mobi_get_record_extrasize(const MOBIPdbRecord *record, const uint16_t flags) {
|
||||
size_t extra_size = 0;
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(record->data, record->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s", "Buffer init in extrasize failed\n");
|
||||
return MOBI_NOTSET;
|
||||
}
|
||||
/* set pointer at the end of the record data */
|
||||
mobi_buffer_setpos(buf, buf->maxlen - 1);
|
||||
for (int bit = 15; bit > 0; bit--) {
|
||||
if (flags & (1 << bit)) {
|
||||
/* bit is set */
|
||||
size_t len = 0;
|
||||
/* size contains varlen itself and optional data */
|
||||
const uint32_t size = mobi_buffer_get_varlen_dec(buf, &len);
|
||||
/* skip data */
|
||||
/* TODO: read and store in record struct */
|
||||
mobi_buffer_seek(buf, - (int)(size - len));
|
||||
extra_size += size;
|
||||
}
|
||||
}
|
||||
/* check bit 0 */
|
||||
if (flags & 1) {
|
||||
const uint8_t b = mobi_buffer_get8(buf);
|
||||
/* two first bits hold size */
|
||||
extra_size += (b & 0x3) + 1;
|
||||
}
|
||||
mobi_buffer_free_null(buf);
|
||||
return extra_size;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Calculate the size of extra multibyte section at the end of text record
|
||||
|
||||
@param[in] record MOBIPdbRecord structure containing the record
|
||||
@param[in] flags Flags from MOBI header (extra_flags)
|
||||
@return The size of trailing bytes, MOBI_NOTSET on failure
|
||||
*/
|
||||
size_t mobi_get_record_mb_extrasize(const MOBIPdbRecord *record, const uint16_t flags) {
|
||||
size_t extra_size = 0;
|
||||
if (flags & 1) {
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(record->data, record->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s", "Buffer init in extrasize failed\n");
|
||||
return MOBI_NOTSET;
|
||||
}
|
||||
/* set pointer at the end of the record data */
|
||||
mobi_buffer_setpos(buf, buf->maxlen - 1);
|
||||
for (int bit = 15; bit > 0; bit--) {
|
||||
if (flags & (1 << bit)) {
|
||||
/* bit is set */
|
||||
size_t len = 0;
|
||||
/* size contains varlen itself and optional data */
|
||||
const uint32_t size = mobi_buffer_get_varlen_dec(buf, &len);
|
||||
/* skip data */
|
||||
/* TODO: read and store in record struct */
|
||||
mobi_buffer_seek(buf, - (int)(size - len));
|
||||
}
|
||||
}
|
||||
/* read multibyte section */
|
||||
const uint8_t b = mobi_buffer_get8(buf);
|
||||
/* two first bits hold size */
|
||||
extra_size += (b & 0x3) + 1;
|
||||
mobi_buffer_free_null(buf);
|
||||
}
|
||||
return extra_size;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse HUFF record into MOBIHuffCdic structure
|
||||
|
||||
@param[in,out] huffcdic MOBIHuffCdic structure to be filled with parsed data
|
||||
@param[in] record MOBIPdbRecord structure containing the record
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_huff(MOBIHuffCdic *huffcdic, const MOBIPdbRecord *record) {
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(record->data, record->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
char huff_magic[5];
|
||||
mobi_buffer_getstring(huff_magic, buf, 4);
|
||||
const size_t header_length = mobi_buffer_get32(buf);
|
||||
if (strncmp(huff_magic, HUFF_MAGIC, 4) != 0 || header_length < HUFF_HEADER_LEN) {
|
||||
debug_print("HUFF wrong magic: %s\n", huff_magic);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const size_t data1_offset = mobi_buffer_get32(buf);
|
||||
const size_t data2_offset = mobi_buffer_get32(buf);
|
||||
/* skip little-endian table offsets */
|
||||
mobi_buffer_setpos(buf, data1_offset);
|
||||
if (buf->offset + (256 * 4) > buf->maxlen) {
|
||||
debug_print("%s", "HUFF data1 too short\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* read 256 indices from data1 big-endian */
|
||||
for (int i = 0; i < 256; i++) {
|
||||
huffcdic->table1[i] = mobi_buffer_get32(buf);
|
||||
}
|
||||
mobi_buffer_setpos(buf, data2_offset);
|
||||
if (buf->offset + (64 * 4) > buf->maxlen) {
|
||||
debug_print("%s", "HUFF data2 too short\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* read 32 mincode-maxcode pairs from data2 big-endian */
|
||||
huffcdic->mincode_table[0] = 0;
|
||||
huffcdic->maxcode_table[0] = 0xFFFFFFFF;
|
||||
for (int i = 1; i < HUFF_CODETABLE_SIZE; i++) {
|
||||
const uint32_t mincode = mobi_buffer_get32(buf);
|
||||
const uint32_t maxcode = mobi_buffer_get32(buf);
|
||||
huffcdic->mincode_table[i] = mincode << (32 - i);
|
||||
huffcdic->maxcode_table[i] = ((maxcode + 1) << (32 - i)) - 1;
|
||||
}
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse CDIC record into MOBIHuffCdic structure
|
||||
|
||||
@param[in,out] huffcdic MOBIHuffCdic structure to be filled with parsed data
|
||||
@param[in] record MOBIPdbRecord structure containing the record
|
||||
@param[in] num Number of CDIC record in a set, starting from zero
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_cdic(MOBIHuffCdic *huffcdic, const MOBIPdbRecord *record, const size_t num) {
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(record->data, record->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
char cdic_magic[5];
|
||||
mobi_buffer_getstring(cdic_magic, buf, 4);
|
||||
const size_t header_length = mobi_buffer_get32(buf);
|
||||
if (strncmp(cdic_magic, CDIC_MAGIC, 4) != 0 || header_length < CDIC_HEADER_LEN) {
|
||||
debug_print("CDIC wrong magic: %s or declared header length: %zu\n", cdic_magic, header_length);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* variables in huffcdic initialized to zero with calloc */
|
||||
/* save initial count and length */
|
||||
size_t index_count = mobi_buffer_get32(buf);
|
||||
const size_t code_length = mobi_buffer_get32(buf);
|
||||
if (huffcdic->code_length && huffcdic->code_length != code_length) {
|
||||
debug_print("CDIC different code length %zu in record %i, previous was %zu\n", huffcdic->code_length, record->uid, code_length);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if (huffcdic->index_count && huffcdic->index_count != index_count) {
|
||||
debug_print("CDIC different index count %zu in record %i, previous was %zu\n", huffcdic->index_count, record->uid, index_count);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if (code_length == 0 || code_length > HUFF_CODELEN_MAX) {
|
||||
debug_print("Code length exceeds sanity checks (%zu)\n", code_length);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
huffcdic->code_length = code_length;
|
||||
huffcdic->index_count = index_count;
|
||||
if (index_count == 0) {
|
||||
debug_print("%s", "CDIC index count is null");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* allocate memory for symbol offsets if not already allocated */
|
||||
if (num == 0) {
|
||||
if (index_count > (1 << HUFF_CODELEN_MAX) * CDIC_RECORD_MAXCNT) {
|
||||
debug_print("CDIC index count too large %zu\n", index_count);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
huffcdic->symbol_offsets = malloc(index_count * sizeof(*huffcdic->symbol_offsets));
|
||||
if (huffcdic->symbol_offsets == NULL) {
|
||||
debug_print("%s", "CDIC cannot allocate memory");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
}
|
||||
index_count -= huffcdic->index_read;
|
||||
/* limit number of records read to code_length bits */
|
||||
if (index_count >> code_length) {
|
||||
index_count = (1 << code_length);
|
||||
}
|
||||
if (buf->offset + (index_count * 2) > buf->maxlen) {
|
||||
debug_print("%s", "CDIC indices data too short\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* read i * 2 byte big-endian indices */
|
||||
while (index_count--) {
|
||||
const uint16_t offset = mobi_buffer_get16(buf);
|
||||
const size_t saved_pos = buf->offset;
|
||||
mobi_buffer_setpos(buf, offset + CDIC_HEADER_LEN);
|
||||
const size_t len = mobi_buffer_get16(buf) & 0x7fff;
|
||||
if (buf->error != MOBI_SUCCESS || buf->offset + len > buf->maxlen) {
|
||||
debug_print("%s", "CDIC offset beyond buffer\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
mobi_buffer_setpos(buf, saved_pos);
|
||||
huffcdic->symbol_offsets[huffcdic->index_read++] = offset;
|
||||
}
|
||||
if (buf->offset + code_length > buf->maxlen) {
|
||||
debug_print("%s", "CDIC dictionary data too short\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
/* copy pointer to data */
|
||||
huffcdic->symbols[num] = record->data + CDIC_HEADER_LEN;
|
||||
/* free buffer */
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse a set of HUFF and CDIC records into MOBIHuffCdic structure
|
||||
|
||||
@param[in] m MOBIData structure with loaded MOBI document
|
||||
@param[in,out] huffcdic MOBIHuffCdic structure to be filled with parsed data
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_huffdic(const MOBIData *m, MOBIHuffCdic *huffcdic) {
|
||||
MOBI_RET ret;
|
||||
const size_t offset = mobi_get_kf8offset(m);
|
||||
if (m->mh == NULL || m->mh->huff_rec_index == NULL || m->mh->huff_rec_count == NULL) {
|
||||
debug_print("%s", "HUFF/CDIC records metadata not found in MOBI header\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const size_t huff_rec_index = *m->mh->huff_rec_index + offset;
|
||||
const size_t huff_rec_count = *m->mh->huff_rec_count;
|
||||
if (huff_rec_count > HUFF_RECORD_MAXCNT) {
|
||||
debug_print("Too many HUFF record (%zu)\n", huff_rec_count);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const MOBIPdbRecord *curr = mobi_get_record_by_seqnumber(m, huff_rec_index);
|
||||
if (curr == NULL || huff_rec_count < 2) {
|
||||
debug_print("%s", "HUFF/CDIC record not found\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if (curr->size < HUFF_RECORD_MINSIZE) {
|
||||
debug_print("HUFF record too short (%zu b)\n", curr->size);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
ret = mobi_parse_huff(huffcdic, curr);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
debug_print("%s", "HUFF parsing failed\n");
|
||||
return ret;
|
||||
}
|
||||
curr = curr->next;
|
||||
/* allocate memory for symbols data in each CDIC record */
|
||||
huffcdic->symbols = malloc((huff_rec_count - 1) * sizeof(*huffcdic->symbols));
|
||||
if (huffcdic->symbols == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
/* get following CDIC records */
|
||||
size_t i = 0;
|
||||
while (i < huff_rec_count - 1) {
|
||||
if (curr == NULL) {
|
||||
debug_print("%s\n", "CDIC record not found");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
ret = mobi_parse_cdic(huffcdic, curr, i++);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
debug_print("%s", "CDIC parsing failed\n");
|
||||
return ret;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
if (huffcdic->index_count != huffcdic->index_read) {
|
||||
debug_print("CDIC: wrong read index count: %zu, total: %zu\n", huffcdic->index_read, huffcdic->index_count);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Parse FDST record into MOBIRawml structure (MOBIFdst member)
|
||||
|
||||
@param[in] m MOBIData structure with loaded MOBI document
|
||||
@param[in,out] rawml MOBIRawml structure to be filled with parsed data
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_parse_fdst(const MOBIData *m, MOBIRawml *rawml) {
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
const size_t fdst_record_number = mobi_get_fdst_record_number(m);
|
||||
if (fdst_record_number == MOBI_NOTSET) {
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
const MOBIPdbRecord *fdst_record = mobi_get_record_by_seqnumber(m, fdst_record_number);
|
||||
if (fdst_record == NULL) {
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
MOBIBuffer *buf = mobi_buffer_init_null(fdst_record->data, fdst_record->size);
|
||||
if (buf == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
char fdst_magic[5];
|
||||
mobi_buffer_getstring(fdst_magic, buf, 4);
|
||||
const size_t data_offset = mobi_buffer_get32(buf);
|
||||
const size_t section_count = mobi_buffer_get32(buf);
|
||||
if (strncmp(fdst_magic, FDST_MAGIC, 4) != 0 ||
|
||||
section_count <= 1 ||
|
||||
section_count != *m->mh->fdst_section_count ||
|
||||
data_offset != 12) {
|
||||
debug_print("FDST wrong magic: %s, sections count: %zu or data offset: %zu\n", fdst_magic, section_count, data_offset);
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
if ((buf->maxlen - buf->offset) < section_count * 8) {
|
||||
debug_print("%s", "Record FDST too short\n");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
rawml->fdst = malloc(sizeof(MOBIFdst));
|
||||
if (rawml->fdst == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
rawml->fdst->fdst_section_count = section_count;
|
||||
rawml->fdst->fdst_section_starts = malloc(sizeof(*rawml->fdst->fdst_section_starts) * section_count);
|
||||
if (rawml->fdst->fdst_section_starts == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
mobi_buffer_free_null(buf);
|
||||
free(rawml->fdst);
|
||||
rawml->fdst = NULL;
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
rawml->fdst->fdst_section_ends = malloc(sizeof(*rawml->fdst->fdst_section_ends) * section_count);
|
||||
if (rawml->fdst->fdst_section_ends == NULL) {
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
mobi_buffer_free_null(buf);
|
||||
free(rawml->fdst->fdst_section_starts);
|
||||
free(rawml->fdst);
|
||||
rawml->fdst = NULL;
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
size_t i = 0;
|
||||
while (i < section_count) {
|
||||
rawml->fdst->fdst_section_starts[i] = mobi_buffer_get32(buf);
|
||||
rawml->fdst->fdst_section_ends[i] = mobi_buffer_get32(buf);
|
||||
debug_print("FDST[%zu]:\t%i\t%i\n", i, rawml->fdst->fdst_section_starts[i], rawml->fdst->fdst_section_ends[i]);
|
||||
i++;
|
||||
}
|
||||
mobi_buffer_free_null(buf);
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read MOBI document from file into MOBIData structure
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with read data
|
||||
@param[in] file File descriptor to read from
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_file(MOBIData *m, FILE *file) {
|
||||
MOBI_RET ret;
|
||||
if (m == NULL) {
|
||||
debug_print("%s", "Mobi structure not initialized\n");
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
ret = mobi_load_pdbheader(m, file);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
if (strcmp(m->ph->type, "BOOK") != 0 && strcmp(m->ph->type, "TEXt") != 0) {
|
||||
debug_print("Unsupported file type: %s\n", m->ph->type);
|
||||
return MOBI_FILE_UNSUPPORTED;
|
||||
}
|
||||
if (m->ph->rec_count == 0) {
|
||||
debug_print("%s", "No records found\n");
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
ret = mobi_load_reclist(m, file);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
ret = mobi_load_rec(m, file);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
ret = mobi_parse_record0(m, 0);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
if (m->rh && m->rh->encryption_type == MOBI_ENCRYPTION_V1) {
|
||||
/* try to set key for encryption type 1 */
|
||||
debug_print("Trying to set key for encryption type 1%s", "\n");
|
||||
mobi_drm_setkey(m, NULL);
|
||||
}
|
||||
/* if EXTH is loaded parse KF8 record0 for hybrid KF7/KF8 file */
|
||||
if (m->eh) {
|
||||
const size_t boundary_rec_number = mobi_get_kf8boundary_seqnumber(m);
|
||||
if (boundary_rec_number != MOBI_NOTSET && boundary_rec_number < UINT32_MAX) {
|
||||
/* it is a hybrid KF7/KF8 file */
|
||||
m->kf8_boundary_offset = (uint32_t) boundary_rec_number;
|
||||
m->next = mobi_init();
|
||||
/* link pdb header and records data to KF8data structure */
|
||||
m->next->ph = m->ph;
|
||||
m->next->rec = m->rec;
|
||||
m->next->drm_key = m->drm_key;
|
||||
m->next->internals = m->internals;
|
||||
/* close next loop */
|
||||
m->next->next = m;
|
||||
ret = mobi_parse_record0(m->next, boundary_rec_number + 1);
|
||||
if (ret != MOBI_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
/* swap to kf8 part if use_kf8 flag is set */
|
||||
if (m->use_kf8) {
|
||||
mobi_swap_mobidata(m);
|
||||
}
|
||||
}
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Read MOBI document from a path into MOBIData structure
|
||||
|
||||
@param[in,out] m MOBIData structure to be filled with read data
|
||||
@param[in] path Path to a MOBI document on disk (eg. /home/me/test.mobi)
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_load_filename(MOBIData *m, const char *path) {
|
||||
FILE *file = fopen(path, "rb");
|
||||
if (file == NULL) {
|
||||
debug_print("%s", "File not found\n");
|
||||
return MOBI_FILE_NOT_FOUND;
|
||||
}
|
||||
const MOBI_RET ret = mobi_load_file(m, file);
|
||||
fclose(file);
|
||||
return ret;
|
||||
}
|
||||
28
app/src/main/cpp/libmobi/src/read.h
vendored
Normal file
28
app/src/main/cpp/libmobi/src/read.h
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
/** @file read.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_read_h
|
||||
#define libmobi_read_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
#include "memory.h"
|
||||
#include "compression.h"
|
||||
|
||||
#define MOBI_EXTH_MAXCNT 1024
|
||||
|
||||
MOBI_RET mobi_parse_fdst(const MOBIData *m, MOBIRawml *rawml);
|
||||
MOBI_RET mobi_parse_huffdic(const MOBIData *m, MOBIHuffCdic *cdic);
|
||||
MOBI_RET mobi_load_pdbheader(MOBIData *m, FILE *file);
|
||||
MOBI_RET mobi_load_reclist(MOBIData *m, FILE *file);
|
||||
MOBI_RET mobi_load_rec(MOBIData *m, FILE *file);
|
||||
MOBI_RET mobi_load_recdata(MOBIPdbRecord *rec, FILE *file);
|
||||
|
||||
#endif
|
||||
281
app/src/main/cpp/libmobi/src/sha1.c
vendored
Normal file
281
app/src/main/cpp/libmobi/src/sha1.c
vendored
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
/*
|
||||
SHA-1 in C
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
100% Public Domain
|
||||
-----------------
|
||||
Modified 7/98
|
||||
By James H. Brown <jbrown@burgoyne.com>
|
||||
Still 100% Public Domain
|
||||
Corrected a problem which generated improper hash values on 16 bit machines
|
||||
Routine SHA1Update changed from
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int
|
||||
len)
|
||||
to
|
||||
void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned
|
||||
long len)
|
||||
The 'len' parameter was declared an int which works fine on 32 bit machines.
|
||||
However, on 16 bit machines an int is too small for the shifts being done
|
||||
against
|
||||
it. This caused the hash function to generate incorrect values if len was
|
||||
greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update().
|
||||
Since the file IO in main() reads 16K at a time, any file 8K or larger would
|
||||
be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million
|
||||
"a"s).
|
||||
I also changed the declaration of variables i & j in SHA1Update to
|
||||
unsigned long from unsigned int for the same reason.
|
||||
These changes should make no difference to any 32 bit implementations since
|
||||
an
|
||||
int and a long are the same size in those environments.
|
||||
--
|
||||
I also corrected a few compiler warnings generated by Borland C.
|
||||
1. Added #include <process.h> for exit() prototype
|
||||
2. Removed unused variable 'j' in SHA1Final
|
||||
3. Changed exit(0) to return(0) at end of main.
|
||||
ALL changes I made can be located by searching for comments containing 'JHB'
|
||||
-----------------
|
||||
Modified 8/98
|
||||
By Steve Reid <sreid@sea-to-sky.net>
|
||||
Still 100% public domain
|
||||
1- Removed #include <process.h> and used return() instead of exit()
|
||||
2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall)
|
||||
3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net
|
||||
-----------------
|
||||
Modified 4/01
|
||||
By Saul Kravitz <Saul.Kravitz@celera.com>
|
||||
Still 100% PD
|
||||
Modified to run on Compaq Alpha hardware.
|
||||
-----------------
|
||||
Modified 07/2002
|
||||
By Ralph Giles <giles@ghostscript.com>
|
||||
Still 100% public domain
|
||||
modified for use with stdint types, autoconf
|
||||
code cleanup, removed attribution comments
|
||||
switched SHA1Final() argument order for consistency
|
||||
use SHA1_ prefix for public api
|
||||
move public api to sha1.h
|
||||
*/
|
||||
|
||||
/*
|
||||
Test Vectors (from FIPS PUB 180-1)
|
||||
"abc"
|
||||
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||
A million repetitions of "a"
|
||||
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||
*/
|
||||
|
||||
/* #define SHA1HANDSOFF */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "sha1.h"
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]);
|
||||
|
||||
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||
|
||||
/* blk0() and blk() perform the initial expand. */
|
||||
/* I got the idea of expanding during the round function from SSLeay */
|
||||
#define blk0(i) (block->l[i] = (((uint32_t)block->c[i*4 ] << 24) | \
|
||||
((uint32_t)block->c[i*4 + 1] << 16) | \
|
||||
((uint32_t)block->c[i*4 + 2] << 8) | \
|
||||
((uint32_t)block->c[i*4 + 3] )))
|
||||
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||
|
||||
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||
|
||||
|
||||
#ifdef VERBOSE /* SAK */
|
||||
void SHAPrintContext(SHA1_CTX *context, char *msg) {
|
||||
printf("%s (%d,%d) %x %x %x %x %x\n",
|
||||
msg,
|
||||
context->count[0], context->count[1],
|
||||
context->state[0],
|
||||
context->state[1],
|
||||
context->state[2],
|
||||
context->state[3],
|
||||
context->state[4]);
|
||||
}
|
||||
#endif /* VERBOSE */
|
||||
|
||||
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||
void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) {
|
||||
uint32_t a, b, c, d, e;
|
||||
typedef union {
|
||||
uint8_t c[64];
|
||||
uint32_t l[16];
|
||||
} CHAR64LONG16;
|
||||
CHAR64LONG16* block;
|
||||
|
||||
#ifdef SHA1HANDSOFF
|
||||
static uint8_t workspace[64];
|
||||
block = (CHAR64LONG16*) workspace;
|
||||
memcpy(block, buffer, 64);
|
||||
#else
|
||||
block = (CHAR64LONG16*) buffer;
|
||||
#endif
|
||||
|
||||
/* Copy context->state[] to working vars */
|
||||
a = state[0];
|
||||
b = state[1];
|
||||
c = state[2];
|
||||
d = state[3];
|
||||
e = state[4];
|
||||
|
||||
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||
|
||||
/* Add the working vars back into context.state[] */
|
||||
state[0] += a;
|
||||
state[1] += b;
|
||||
state[2] += c;
|
||||
state[3] += d;
|
||||
state[4] += e;
|
||||
|
||||
/* Wipe variables */
|
||||
a = b = c = d = e = 0;
|
||||
UNUSED(a);UNUSED(b);UNUSED(c);UNUSED(d);UNUSED(e);
|
||||
}
|
||||
|
||||
|
||||
/* SHA1Init - Initialize new context */
|
||||
void SHA1_Init(SHA1_CTX* context) {
|
||||
/* SHA1 initialization constants */
|
||||
context->state[0] = 0x67452301;
|
||||
context->state[1] = 0xEFCDAB89;
|
||||
context->state[2] = 0x98BADCFE;
|
||||
context->state[3] = 0x10325476;
|
||||
context->state[4] = 0xC3D2E1F0;
|
||||
context->count[0] = context->count[1] = 0;
|
||||
}
|
||||
|
||||
|
||||
/* Run your data through this. */
|
||||
void SHA1_Update(SHA1_CTX* context, const uint8_t* data, const size_t len) {
|
||||
size_t i, j;
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "before");
|
||||
#endif
|
||||
|
||||
j = (context->count[0] >> 3) & 63;
|
||||
if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
|
||||
context->count[1] += (len >> 29);
|
||||
if ((j + len) > 63) {
|
||||
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
for ( ; i + 63 < len; i += 64) {
|
||||
SHA1_Transform(context->state, data + i);
|
||||
}
|
||||
j = 0;
|
||||
}
|
||||
else i = 0;
|
||||
memcpy(&context->buffer[j], &data[i], len - i);
|
||||
|
||||
#ifdef VERBOSE
|
||||
SHAPrintContext(context, "after ");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/* Add padding and return the message digest. */
|
||||
void SHA1_Final(SHA1_CTX* context, uint8_t digest[SHA1_DIGEST_SIZE]) {
|
||||
uint32_t i;
|
||||
uint8_t finalcount[8];
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||
}
|
||||
SHA1_Update(context, (uint8_t *)"\200", 1);
|
||||
while ((context->count[0] & 504) != 448) {
|
||||
SHA1_Update(context, (uint8_t *)"\0", 1);
|
||||
}
|
||||
SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE; i++) {
|
||||
digest[i] = (uint8_t)
|
||||
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||
}
|
||||
|
||||
/* Wipe variables */
|
||||
i = 0;
|
||||
UNUSED(i);
|
||||
memset(context->buffer, 0, 64);
|
||||
memset(context->state, 0, 20);
|
||||
memset(context->count, 0, 8);
|
||||
memset(finalcount, 0, 8); /* SWR */
|
||||
|
||||
#ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */
|
||||
SHA1_Transform(context->state, context->buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*************************************************************/
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char** argv) {
|
||||
int i, j;
|
||||
SHA1_CTX context;
|
||||
unsigned char digest[SHA1_DIGEST_SIZE], buffer[16384];
|
||||
FILE* file;
|
||||
if (argc > 2) {
|
||||
puts("Public domain SHA-1 implementation - by Steve Reid <sreid@sea-to-sky.net>");
|
||||
puts("Modified for 16 bit environments 7/98 - by James H. Brown <jbrown@burgoyne.com>"); /* JHB */
|
||||
puts("Produces the SHA-1 hash of a file, or stdin if no file is specified.");
|
||||
return(0);
|
||||
}
|
||||
if (argc < 2) {
|
||||
file = stdin;
|
||||
}
|
||||
else {
|
||||
if (!(file = fopen(argv[1], "rb"))) {
|
||||
fputs("Unable to open file.", stderr);
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
SHA1_Init(&context);
|
||||
while (!feof(file)) { /* note: what if ferror(file) */
|
||||
i = fread(buffer, 1, 16384, file);
|
||||
SHA1_Update(&context, buffer, i);
|
||||
}
|
||||
SHA1_Final(&context, digest);
|
||||
fclose(file);
|
||||
for (i = 0; i < SHA1_DIGEST_SIZE/4; i++) {
|
||||
for (j = 0; j < 4; j++) {
|
||||
printf("%02X", digest[i*4+j]);
|
||||
}
|
||||
putchar(' ');
|
||||
}
|
||||
putchar('\n');
|
||||
return(0); /* JHB */
|
||||
}
|
||||
#endif
|
||||
27
app/src/main/cpp/libmobi/src/sha1.h
vendored
Normal file
27
app/src/main/cpp/libmobi/src/sha1.h
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/** @file sha1.h
|
||||
* @brief Header for sha1.c
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_sha1_h
|
||||
#define mobi_sha1_h
|
||||
|
||||
typedef struct {
|
||||
uint32_t state[5];
|
||||
uint32_t count[2];
|
||||
uint8_t buffer[64];
|
||||
} SHA1_CTX;
|
||||
|
||||
#define SHA1_DIGEST_SIZE 20
|
||||
|
||||
void SHA1_Init(SHA1_CTX *context);
|
||||
void SHA1_Update(SHA1_CTX *context, const uint8_t *data, const size_t len);
|
||||
void SHA1_Final(SHA1_CTX *context, uint8_t digest[SHA1_DIGEST_SIZE]);
|
||||
|
||||
#endif /* sha1_h */
|
||||
566
app/src/main/cpp/libmobi/src/structure.c
vendored
Normal file
566
app/src/main/cpp/libmobi/src/structure.c
vendored
Normal file
|
|
@ -0,0 +1,566 @@
|
|||
/** @file structure.c
|
||||
* @brief Data structures
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "structure.h"
|
||||
#include "debug.h"
|
||||
#if defined(__BIONIC__) && !defined(SIZE_MAX)
|
||||
#include <limits.h> /* for SIZE_MAX */
|
||||
#endif
|
||||
|
||||
/**
|
||||
@brief Initializer for MOBIArray structure
|
||||
|
||||
It allocates memory for structure and for data: array of uint32_t variables.
|
||||
Memory should be freed with array_free().
|
||||
|
||||
@param[in] len Initial size of the array
|
||||
@return MOBIArray on success, NULL otherwise
|
||||
*/
|
||||
MOBIArray * array_init(const size_t len) {
|
||||
MOBIArray *arr = NULL;
|
||||
arr = malloc(sizeof(MOBIArray));
|
||||
if (arr == NULL) {
|
||||
debug_print("%s", "Array allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
arr->data = malloc(len * sizeof(*arr->data));
|
||||
if (arr->data == NULL) {
|
||||
free(arr);
|
||||
debug_print("%s", "Array data allocation failed\n");
|
||||
return NULL;
|
||||
}
|
||||
arr->maxsize = len;
|
||||
arr->step = len ? len : 1;
|
||||
arr->size = 0;
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Inserts value into MOBIArray array
|
||||
|
||||
@param[in,out] arr MOBIArray array
|
||||
@param[in] value Value to be inserted
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET array_insert(MOBIArray *arr, const uint32_t value) {
|
||||
if (!arr || arr->maxsize == 0) {
|
||||
return MOBI_INIT_FAILED;
|
||||
}
|
||||
if (arr->maxsize == arr->size) {
|
||||
arr->maxsize += arr->step;
|
||||
uint32_t *tmp = realloc(arr->data, arr->maxsize * sizeof(*arr->data));
|
||||
if (!tmp) {
|
||||
free(arr->data);
|
||||
arr->data = NULL;
|
||||
debug_print("%s\n", "Memory allocation failed");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
arr->data = tmp;
|
||||
}
|
||||
arr->data[arr->size] = value;
|
||||
arr->size++;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Helper for qsort in array_sort() function.
|
||||
|
||||
@param[in] a First element to compare
|
||||
@param[in] b Second element to compare
|
||||
@return -1 if a < b; 1 if a > b; 0 if a = b
|
||||
*/
|
||||
static int array_compare(const void *a, const void *b) {
|
||||
if (*(uint32_t *) a < *(uint32_t *) b) {
|
||||
return -1;
|
||||
};
|
||||
if (*(uint32_t *) a > *(uint32_t *) b) {
|
||||
return 1;
|
||||
};
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Sort MOBIArray in ascending order.
|
||||
|
||||
When unique is set to true, duplicate values are discarded.
|
||||
|
||||
@param[in,out] arr MOBIArray array
|
||||
@param[in] unique Discard duplicate values if true
|
||||
*/
|
||||
void array_sort(MOBIArray *arr, const bool unique) {
|
||||
if (!arr || !arr->data || arr->size == 0) {
|
||||
return;
|
||||
}
|
||||
qsort(arr->data, arr->size, sizeof(*arr->data), array_compare);
|
||||
if (unique) {
|
||||
size_t i = 1;
|
||||
size_t j = 1;
|
||||
while (i < arr->size) {
|
||||
if (arr->data[j - 1] == arr->data[i]) {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
arr->data[j++] = arr->data[i++];
|
||||
}
|
||||
arr->size = j;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Get size of the array
|
||||
|
||||
@param[in] arr MOBIArray structure
|
||||
@return Array size
|
||||
*/
|
||||
size_t array_size(MOBIArray *arr) {
|
||||
return arr->size;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Free MOBIArray structure and contained data
|
||||
|
||||
Free data initialized with array_init();
|
||||
|
||||
@param[in] arr MOBIArray structure
|
||||
*/
|
||||
void array_free(MOBIArray *arr) {
|
||||
if (!arr) { return; }
|
||||
if (arr->data) {
|
||||
free(arr->data);
|
||||
}
|
||||
free(arr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@brief Create and return MOBITrie structure
|
||||
|
||||
@return MOBITrie stucture initialized with zeroes
|
||||
*/
|
||||
static MOBITrie * mobi_trie_mknode(void) {
|
||||
MOBITrie *node = calloc(1, sizeof(MOBITrie));
|
||||
if (node == NULL) {
|
||||
debug_print("Memory allocation failed%s", "\n");
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Recursively free MOBITrie trie starting from node
|
||||
|
||||
@param[in] node Starting node
|
||||
*/
|
||||
void mobi_trie_free(MOBITrie *node) {
|
||||
if (node) {
|
||||
mobi_trie_free(node->next);
|
||||
mobi_trie_free(node->children);
|
||||
free(node->values);
|
||||
free(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Insert value into array at given MOBITrie node
|
||||
|
||||
@param[in,out] node Starting node
|
||||
@param[in] value Value to be inserted
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
static MOBI_RET mobi_trie_addvalue(MOBITrie *node, char *value) {
|
||||
if (node->values) {
|
||||
size_t cnt = ++node->values_count;
|
||||
void *new_values = realloc(node->values, cnt * sizeof(*node->values));
|
||||
if (new_values == NULL) {
|
||||
debug_print("Memory allocation failed%s", "\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
node->values = new_values;
|
||||
node->values[cnt - 1] = value;
|
||||
} else {
|
||||
node->values = malloc(sizeof(*node->values));
|
||||
if (node->values == NULL) {
|
||||
debug_print("Memory allocation failed%s", "\n");
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
node->values[0] = value;
|
||||
node->values_count = 1;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Insert key character and value (if given) at MOBITrie node
|
||||
|
||||
@param[in,out] node Starting node
|
||||
@param[in] c Key character
|
||||
@param[in] value Value to be inserted at terminal node, or NULL if not terminal
|
||||
@return MOBITrie node: current node if value inserted (terminal),
|
||||
children node (if transitional) or NULL on error
|
||||
*/
|
||||
static MOBITrie * mobi_trie_insert_char(MOBITrie *node, char c, char *value) {
|
||||
if (!node) { return NULL; }
|
||||
while (true) {
|
||||
if (node->c == c) {
|
||||
break;
|
||||
}
|
||||
if (node->next == NULL) {
|
||||
node->next = mobi_trie_mknode();
|
||||
node = node->next;
|
||||
break;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
if (node->c == 0) {
|
||||
node->c = c;
|
||||
}
|
||||
if (value) {
|
||||
/* terminal node */
|
||||
if (mobi_trie_addvalue(node, value) == MOBI_SUCCESS) {
|
||||
return node;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (node->children == NULL) {
|
||||
node->children = mobi_trie_mknode();
|
||||
}
|
||||
return node->children;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Insert reversed string into MOBITrie trie
|
||||
|
||||
@param[in,out] root Root node
|
||||
@param[in] string String to be inserted
|
||||
@param[in] value Value associated with the string
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_trie_insert_reversed(MOBITrie **root, char *string, char *value) {
|
||||
size_t length = strlen(string);
|
||||
if (length == 0) {
|
||||
debug_print("Skipping empty lookup string in trie node%s", "\n");
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
if (*root == NULL) {
|
||||
*root = mobi_trie_mknode();
|
||||
if (*root == NULL) {
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
}
|
||||
MOBITrie *node = *root;
|
||||
while (length > 1) {
|
||||
node = mobi_trie_insert_char(node, string[length - 1], NULL);
|
||||
if (node == NULL) {
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
length--;
|
||||
}
|
||||
node = mobi_trie_insert_char(node, string[length - 1], value);
|
||||
if (node == NULL) {
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Fetch values for key c from MOBITrie trie's current level starting at node
|
||||
|
||||
@param[in,out] values Array of values to be fetched
|
||||
@param[in,out] values_count Array size
|
||||
@param[in] node MOBITrie node to start search
|
||||
@param[in] c Key character
|
||||
@return MOBITrie children node of the node with c key or NULL if not found
|
||||
*/
|
||||
MOBITrie * mobi_trie_get_next(char ***values, size_t *values_count, const MOBITrie *node, const char c) {
|
||||
if (!node) { return NULL; }
|
||||
while (node) {
|
||||
if (node->c == c) {
|
||||
*values = (char**) node->values;
|
||||
*values_count = node->values_count;
|
||||
return node->children;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if 0
|
||||
/* Simple imprementation of binary tree, storing key strings
|
||||
and associated arrays of values
|
||||
currently not used, save for later */
|
||||
|
||||
typedef struct MOBIBtree {
|
||||
char *key; /**< key */
|
||||
char **array; /**< array of strings */
|
||||
size_t value_count; /**< strings count */
|
||||
struct MOBIBtree *left; /**< left child */
|
||||
struct MOBIBtree *right; /**< right child */
|
||||
} MOBIBtree;
|
||||
|
||||
/**
|
||||
@brief Search MOBIBtree tree for string key
|
||||
|
||||
@param[in] node MOBIBtree node to start search
|
||||
@param[in] key Key string
|
||||
@return MOBIBtree node or NULL if not found
|
||||
*/
|
||||
MOBIBtree *mobi_btree_search(MOBIBtree *node, const char *key) {
|
||||
MOBIBtree *found = NULL;
|
||||
int compare = strcmp(key, node->key);
|
||||
if (compare < 0) {
|
||||
found = mobi_btree_search(node->left, key);
|
||||
} else if (compare > 0) {
|
||||
found = mobi_btree_search(node->right, key);
|
||||
} else {
|
||||
found = node;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Insert key and value (into array) into MOBIBtree tree
|
||||
|
||||
@param[in] node MOBIBtree root node
|
||||
@param[in] key Key string
|
||||
@param[in] value Value string will be inserted into array
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS)
|
||||
*/
|
||||
MOBI_RET mobi_btree_insert(MOBIBtree **node, char *key, char *value) {
|
||||
MOBI_RET ret = MOBI_SUCCESS;
|
||||
if (*node == NULL) {
|
||||
*node = malloc(sizeof(MOBIBtree));
|
||||
if (*node == NULL) {
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
(*node)->key = key;
|
||||
(*node)->value_count = 1;
|
||||
(*node)->array = malloc(sizeof(*(*node)->array));
|
||||
if ((*node)->array == NULL) {
|
||||
free(*node);
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
(*node)->array[0] = value;
|
||||
(*node)->left = NULL;
|
||||
(*node)->right = NULL;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
int compare = strcmp(key, (*node)->key);
|
||||
if (compare < 0) {
|
||||
ret = mobi_btree_insert(&(*node)->left, key, value);
|
||||
} else if (compare > 0) {
|
||||
ret = mobi_btree_insert(&(*node)->right, key, value);
|
||||
} else {
|
||||
size_t cnt = ++(*node)->value_count;
|
||||
char **new_array = realloc((*node)->array, cnt * sizeof(*(*node)->array));
|
||||
if (new_array) {
|
||||
(*node)->array = new_array;
|
||||
(*node)->array[cnt - 1] = value;
|
||||
} else {
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
@brief Allocate fragment, fill with data and return
|
||||
|
||||
@param[in] raw_offset Fragment offset in raw markup,
|
||||
SIZE_MAX if not present in original markup
|
||||
@param[in] fragment Fragment data
|
||||
@param[in] size Size data
|
||||
@param[in] is_malloc is_maloc data
|
||||
@return Fragment structure filled with data
|
||||
*/
|
||||
static MOBIFragment * mobi_list_init(size_t raw_offset, unsigned char *fragment, const size_t size, const bool is_malloc) {
|
||||
MOBIFragment *curr = calloc(1, sizeof(MOBIFragment));
|
||||
if (curr == NULL) {
|
||||
if (is_malloc) {
|
||||
free(fragment);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
curr->raw_offset = raw_offset;
|
||||
curr->fragment = fragment;
|
||||
curr->size = size;
|
||||
curr->is_malloc = is_malloc;
|
||||
return curr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Allocate fragment, fill with data, append to linked list
|
||||
|
||||
@param[in] curr Last fragment in linked list
|
||||
@param[in] raw_offset Fragment offset in raw markup,
|
||||
SIZE_MAX if not present in original markup
|
||||
@param[in] fragment Fragment data
|
||||
@param[in] size Size data
|
||||
@param[in] is_malloc is_maloc data
|
||||
@return Fragment structure filled with data
|
||||
*/
|
||||
MOBIFragment * mobi_list_add(MOBIFragment *curr, size_t raw_offset, unsigned char *fragment, const size_t size, const bool is_malloc) {
|
||||
if (!curr) {
|
||||
return mobi_list_init(raw_offset, fragment, size, is_malloc);
|
||||
}
|
||||
curr->next = calloc(1, sizeof(MOBIFragment));
|
||||
if (curr->next == NULL) {
|
||||
if (is_malloc) {
|
||||
free(fragment);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
MOBIFragment *next = curr->next;
|
||||
next->raw_offset = raw_offset;
|
||||
next->fragment = fragment;
|
||||
next->size = size;
|
||||
next->is_malloc = is_malloc;
|
||||
return next;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Allocate fragment, fill with data,
|
||||
insert into linked list at given offset
|
||||
|
||||
Starts to search for offset at given fragment. The pointer to input fragment will be replaced by newly added one.
|
||||
|
||||
@param[in,out] fragment Fragment where search starts, on success pointer to new fragment structure filled with data
|
||||
@param[in] raw_offset Fragment offset in raw markup, SIZE_MAX if not present in original markup
|
||||
@param[in] data Fragment data
|
||||
@param[in] size Size data
|
||||
@param[in] is_malloc is_maloc data
|
||||
@param[in] offset offset where new chunk will be inserted
|
||||
@return MOBI_RET status code (on success MOBI_SUCCESS, on offset not found MOBI_DATA_CORRUPT)
|
||||
*/
|
||||
MOBI_RET mobi_list_insert(MOBIFragment **fragment, size_t raw_offset, unsigned char *data, const size_t size, const bool is_malloc, const size_t offset) {
|
||||
MOBIFragment *curr = *fragment;
|
||||
MOBIFragment *prev = NULL;
|
||||
while (curr) {
|
||||
if (curr->raw_offset != SIZE_MAX && curr->raw_offset <= offset && curr->raw_offset + curr->size >= offset ) {
|
||||
break;
|
||||
}
|
||||
prev = curr;
|
||||
curr = curr->next;
|
||||
}
|
||||
if (!curr) {
|
||||
debug_print("Offset not found: %zu\n", offset);
|
||||
if (is_malloc) {
|
||||
free(data);
|
||||
}
|
||||
return MOBI_DATA_CORRUPT;
|
||||
}
|
||||
MOBIFragment *new = calloc(1, sizeof(MOBIFragment));
|
||||
if (new == NULL) {
|
||||
if (is_malloc) {
|
||||
free(data);
|
||||
}
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
new->raw_offset = raw_offset;
|
||||
new->fragment = data;
|
||||
new->size = size;
|
||||
new->is_malloc = is_malloc;
|
||||
MOBIFragment *new2 = NULL;
|
||||
if (curr->raw_offset == offset) {
|
||||
/* prepend chunk */
|
||||
if (prev) {
|
||||
prev->next = new;
|
||||
new->next = curr;
|
||||
} else {
|
||||
/* save curr */
|
||||
MOBIFragment tmp;
|
||||
tmp.raw_offset = curr->raw_offset;
|
||||
tmp.fragment = curr->fragment;
|
||||
tmp.size = curr->size;
|
||||
tmp.is_malloc = curr->is_malloc;
|
||||
tmp.next = curr->next;
|
||||
/* move new to curr */
|
||||
curr->raw_offset = new->raw_offset;
|
||||
curr->fragment = new->fragment;
|
||||
curr->size = new->size;
|
||||
curr->is_malloc = new->is_malloc;
|
||||
curr->next = new;
|
||||
/* restore tmp to new */
|
||||
new->raw_offset = tmp.raw_offset;
|
||||
new->fragment = tmp.fragment;
|
||||
new->size = tmp.size;
|
||||
new->is_malloc = tmp.is_malloc;
|
||||
new->next = tmp.next;
|
||||
*fragment = curr;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
} else if (curr->raw_offset + curr->size == offset) {
|
||||
/* append chunk */
|
||||
new->next = curr->next;
|
||||
curr->next = new;
|
||||
} else {
|
||||
/* split fragment and insert new chunk */
|
||||
new2 = calloc(1, sizeof(MOBIFragment));
|
||||
if (new2 == NULL) {
|
||||
free(new);
|
||||
if (is_malloc) {
|
||||
free(data);
|
||||
}
|
||||
return MOBI_MALLOC_FAILED;
|
||||
}
|
||||
size_t rel_offset = offset - curr->raw_offset;
|
||||
new2->next = curr->next;
|
||||
new2->size = curr->size - rel_offset;
|
||||
new2->raw_offset = offset;
|
||||
new2->fragment = curr->fragment + rel_offset;
|
||||
new2->is_malloc = false;
|
||||
curr->next = new;
|
||||
curr->size = rel_offset;
|
||||
new->next = new2;
|
||||
}
|
||||
/* correct offsets */
|
||||
if (raw_offset != SIZE_MAX) {
|
||||
curr = new->next;
|
||||
while (curr) {
|
||||
if (curr->raw_offset != SIZE_MAX) {
|
||||
curr->raw_offset += new->size;
|
||||
}
|
||||
curr = curr->next;
|
||||
}
|
||||
}
|
||||
*fragment = new;
|
||||
return MOBI_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete fragment from linked list
|
||||
|
||||
@param[in] curr Fragment to be deleted
|
||||
@return Next fragment in the linked list or NULL if absent
|
||||
*/
|
||||
MOBIFragment * mobi_list_del(MOBIFragment *curr) {
|
||||
MOBIFragment *del = curr;
|
||||
curr = curr->next;
|
||||
if (del->is_malloc) {
|
||||
free(del->fragment);
|
||||
}
|
||||
free(del);
|
||||
del = NULL;
|
||||
return curr;
|
||||
}
|
||||
|
||||
/**
|
||||
@brief Delete all fragments from linked list
|
||||
|
||||
@param[in] first First fragment from the list
|
||||
*/
|
||||
void mobi_list_del_all(MOBIFragment *first) {
|
||||
while (first) {
|
||||
first = mobi_list_del(first);
|
||||
}
|
||||
}
|
||||
66
app/src/main/cpp/libmobi/src/structure.h
vendored
Normal file
66
app/src/main/cpp/libmobi/src/structure.h
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/** @file structure.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef mobi_structure_h
|
||||
#define mobi_structure_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
|
||||
/**
|
||||
@brief Dynamic array of uint32_t values structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t *data; /**< Array */
|
||||
size_t maxsize; /**< Allocated size */
|
||||
size_t step; /**< Step by which array will be enlarged if out of memory */
|
||||
size_t size; /**< Current size */
|
||||
} MOBIArray;
|
||||
|
||||
MOBIArray * array_init(const size_t len);
|
||||
MOBI_RET array_insert(MOBIArray *arr, const uint32_t value);
|
||||
void array_sort(MOBIArray *arr, const bool unique);
|
||||
size_t array_size(MOBIArray *arr);
|
||||
void array_free(MOBIArray *arr);
|
||||
|
||||
/**
|
||||
@brief Trie storing arrays of values for character keys
|
||||
*/
|
||||
typedef struct MOBITrie {
|
||||
char c; /**< Key character */
|
||||
void **values; /**< Array of values */
|
||||
size_t values_count; /**< Array size */
|
||||
struct MOBITrie *next; /**< Next node at the same level */
|
||||
struct MOBITrie *children; /**< Link to children nodes, lower level */
|
||||
} MOBITrie;
|
||||
|
||||
MOBI_RET mobi_trie_insert_reversed(MOBITrie **root, char *string, char *value);
|
||||
MOBITrie * mobi_trie_get_next(char ***values, size_t *values_count, const MOBITrie *node, const char c);
|
||||
void mobi_trie_free(MOBITrie *node);
|
||||
|
||||
/**
|
||||
@brief Structure for links reconstruction.
|
||||
|
||||
Linked list of Fragment structures forms whole document part
|
||||
*/
|
||||
typedef struct MOBIFragment {
|
||||
size_t raw_offset; /**< fragment offset in raw markup, SIZE_MAX if not present in original markup */
|
||||
unsigned char *fragment; /**< Fragment data */
|
||||
size_t size; /**< Fragment size */
|
||||
bool is_malloc; /**< Is it needed to free this fragment or is it just an alias to part data */
|
||||
struct MOBIFragment *next; /**< Link to next fragment */
|
||||
} MOBIFragment;
|
||||
|
||||
MOBIFragment * mobi_list_add(MOBIFragment *curr, size_t raw_offset, unsigned char *fragment, const size_t size, const bool is_malloc);
|
||||
MOBIFragment * mobi_list_del(MOBIFragment *curr);
|
||||
MOBI_RET mobi_list_insert(MOBIFragment **curr, size_t raw_offset, unsigned char *fragment, const size_t size, const bool is_malloc, const size_t offset);
|
||||
void mobi_list_del_all(MOBIFragment *first);
|
||||
|
||||
#endif
|
||||
3561
app/src/main/cpp/libmobi/src/util.c
vendored
Normal file
3561
app/src/main/cpp/libmobi/src/util.c
vendored
Normal file
File diff suppressed because it is too large
Load diff
157
app/src/main/cpp/libmobi/src/util.h
vendored
Normal file
157
app/src/main/cpp/libmobi/src/util.h
vendored
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/** @file util.h
|
||||
*
|
||||
* Copyright (c) 2014 Bartek Fabiszewski
|
||||
* http://www.fabiszewski.net
|
||||
*
|
||||
* This file is part of libmobi.
|
||||
* Licensed under LGPL, either version 3, or any later.
|
||||
* See <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef libmobi_util_h
|
||||
#define libmobi_util_h
|
||||
|
||||
#include "config.h"
|
||||
#include "mobi.h"
|
||||
#include "memory.h"
|
||||
#include "buffer.h"
|
||||
#include "read.h"
|
||||
#include "compression.h"
|
||||
|
||||
#ifndef HAVE_STRDUP
|
||||
/** @brief strdup replacement */
|
||||
#define strdup mobi_strdup
|
||||
#endif
|
||||
|
||||
#ifdef USE_MINIZ
|
||||
#include "miniz.h"
|
||||
#define m_uncompress mz_uncompress
|
||||
#define m_crc32 mz_crc32
|
||||
#define M_OK MZ_OK
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#define m_uncompress uncompress
|
||||
#define m_crc32 crc32
|
||||
#define M_OK Z_OK
|
||||
#endif
|
||||
|
||||
#define UNUSED(x) (void)(x)
|
||||
|
||||
/** @brief Magic numbers of records */
|
||||
#define AUDI_MAGIC "AUDI"
|
||||
#define CDIC_MAGIC "CDIC"
|
||||
#define CMET_MAGIC "CMET"
|
||||
#define EXTH_MAGIC "EXTH"
|
||||
#define FDST_MAGIC "FDST"
|
||||
#define FONT_MAGIC "FONT"
|
||||
#define HUFF_MAGIC "HUFF"
|
||||
#define IDXT_MAGIC "IDXT"
|
||||
#define INDX_MAGIC "INDX"
|
||||
#define LIGT_MAGIC "LIGT"
|
||||
#define MOBI_MAGIC "MOBI"
|
||||
#define ORDT_MAGIC "ORDT"
|
||||
#define RESC_MAGIC "RESC"
|
||||
#define SRCS_MAGIC "SRCS"
|
||||
#define TAGX_MAGIC "TAGX"
|
||||
#define VIDE_MAGIC "VIDE"
|
||||
#define BOUNDARY_MAGIC "BOUNDARY"
|
||||
#define EOF_MAGIC "\xe9\x8e\r\n"
|
||||
#define REPLICA_MAGIC "%MOP"
|
||||
|
||||
/** @brief Difference in seconds between epoch time and mac time */
|
||||
#define EPOCH_MAC_DIFF 2082844800UL
|
||||
|
||||
/**
|
||||
@defgroup mobi_pdb Params for pdb record header structure
|
||||
@{
|
||||
*/
|
||||
#define PALMDB_HEADER_LEN 78 /**< Length of header without record info headers */
|
||||
#define PALMDB_NAME_SIZE_MAX 32 /**< Max length of db name stored at offset 0 */
|
||||
#define PALMDB_RECORD_INFO_SIZE 8 /**< Record info header size of each pdb record */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@defgroup mobi_pdb_defs Default values for pdb record header structure
|
||||
@{
|
||||
*/
|
||||
#define PALMDB_ATTRIBUTE_DEFAULT 0
|
||||
#define PALMDB_VERSION_DEFAULT 0
|
||||
#define PALMDB_MODNUM_DEFAULT 0
|
||||
#define PALMDB_APPINFO_DEFAULT 0
|
||||
#define PALMDB_SORTINFO_DEFAULT 0
|
||||
#define PALMDB_TYPE_DEFAULT "BOOK"
|
||||
#define PALMDB_CREATOR_DEFAULT "MOBI"
|
||||
#define PALMDB_NEXTREC_DEFAULT 0
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@defgroup mobi_rec0 Params for record0 header structure
|
||||
@{
|
||||
*/
|
||||
#define RECORD0_HEADER_LEN 16 /**< Length of Record 0 header */
|
||||
#define RECORD0_TEXT_SIZE_MAX 4096 /**< Max size of uncompressed text record */
|
||||
#define RECORD0_FULLNAME_SIZE_MAX 1024 /**< Max size to full name string */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@defgroup mobi_len Header length / size of records
|
||||
@{
|
||||
*/
|
||||
#define CDIC_HEADER_LEN 16
|
||||
#define CDIC_RECORD_MAXCNT 1024
|
||||
#define HUFF_CODELEN_MAX 16
|
||||
#define HUFF_HEADER_LEN 24
|
||||
#define HUFF_RECORD_MAXCNT 1024
|
||||
#define HUFF_RECORD_MINSIZE 2584
|
||||
#define FONT_HEADER_LEN 24
|
||||
#define MEDIA_HEADER_LEN 12
|
||||
#define FONT_SIZEMAX (50 * 1024 * 1024)
|
||||
#define RAWTEXT_SIZEMAX 0xfffffff
|
||||
#define MOBI_HEADER_V2_SIZE 0x18
|
||||
#define MOBI_HEADER_V3_SIZE 0x74
|
||||
#define MOBI_HEADER_V4_SIZE 0xd0
|
||||
#define MOBI_HEADER_V5_SIZE 0xe4
|
||||
#define MOBI_HEADER_V6_SIZE 0xe4
|
||||
#define MOBI_HEADER_V6_EXT_SIZE 0xe8
|
||||
#define MOBI_HEADER_V7_SIZE 0xe4
|
||||
/** @} */
|
||||
|
||||
#ifndef max
|
||||
#define max(a, b) ((a) > (b) ? (a) : (b))
|
||||
#endif
|
||||
#ifndef min
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
#endif
|
||||
|
||||
#define ARRAYSIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||
|
||||
#define MOBI_TITLE_SIZEMAX 1024
|
||||
|
||||
int mobi_bitcount(const uint8_t byte);
|
||||
MOBI_RET mobi_delete_record_by_seqnumber(MOBIData *m, const size_t num);
|
||||
MOBI_RET mobi_swap_mobidata(MOBIData *m);
|
||||
char * mobi_strdup(const char *s);
|
||||
bool mobi_is_cp1252(const MOBIData *m);
|
||||
bool mobi_has_drmkey(const MOBIData *m);
|
||||
bool mobi_has_drmcookies(const MOBIData *m);
|
||||
MOBI_RET mobi_cp1252_to_utf8(char *output, const char *input, size_t *outsize, const size_t insize);
|
||||
MOBI_RET mobi_utf8_to_cp1252(char *output, const char *input, size_t *outsize, const size_t insize);
|
||||
uint8_t mobi_ligature_to_cp1252(const uint8_t byte1, const uint8_t byte2);
|
||||
uint16_t mobi_ligature_to_utf16(const uint32_t byte1, const uint32_t byte2);
|
||||
MOBIFiletype mobi_determine_resource_type(const MOBIPdbRecord *record);
|
||||
MOBIFiletype mobi_determine_flowpart_type(const MOBIRawml *rawml, const size_t part_number);
|
||||
MOBI_RET mobi_base32_decode(uint32_t *decoded, const char *encoded);
|
||||
MOBIFiletype mobi_get_resourcetype_by_uid(const MOBIRawml *rawml, const size_t uid);
|
||||
uint32_t mobi_get_exthsize(const MOBIData *m);
|
||||
uint32_t mobi_get_drmsize(const MOBIData *m);
|
||||
uint16_t mobi_get_records_count(const MOBIData *m);
|
||||
void mobi_remove_zeros(unsigned char *buffer, size_t *len);
|
||||
MOBI_RET mobi_add_audio_resource(MOBIPart *part);
|
||||
MOBI_RET mobi_add_video_resource(MOBIPart *part);
|
||||
MOBI_RET mobi_add_font_resource(MOBIPart *part);
|
||||
MOBI_RET mobi_set_fullname(MOBIData *m, const char *fullname);
|
||||
MOBI_RET mobi_set_pdbname(MOBIData *m, const char *name);
|
||||
void mobi_free_internals(MOBIData *m);
|
||||
uint32_t mobi_get32be(const unsigned char buf[4]);
|
||||
uint32_t mobi_get32le(const unsigned char buf[4]);
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue