Initial commit
This commit is contained in:
commit
6072b2ba29
844 changed files with 220532 additions and 0 deletions
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
app/src/main/cpp/** linguist-vendored
|
||||||
|
pdfiumandroid/** linguist-vendored
|
||||||
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
|
.cxx
|
||||||
|
local.properties
|
||||||
|
.env
|
||||||
|
*.jks
|
||||||
|
google-services.json
|
||||||
|
.kotlin/
|
||||||
|
.idea/
|
||||||
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
|
||||||
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