SASPlanet sqlite parser

This commit is contained in:
Alexey 2025-11-06 14:58:28 +03:00
commit 516455209e
4 changed files with 98 additions and 2 deletions

2
TODO
View file

@ -1,5 +1,5 @@
functionality:
sasplanet zipped sqlitedbs parser
(done) sasplanet zipped sqlitedbs parser
refactor:
extract file opener from settings activity
extract zip parser from settings activity

View file

@ -29,6 +29,7 @@ class KhmParser {
ctx.openFileOutput(HEIGHT_FILE, Context.MODE_PRIVATE).use {
it.write(inp.readBytes())
}
inp.close()
}
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {

View file

@ -3,6 +3,7 @@ package com.mirenkov.ktheightmap
import android.app.Application
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
@ -22,10 +23,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
import androidx.lifecycle.viewmodel.compose.viewModel
import com.mirenkov.ktheightmap.parser.SasSQLiteParser
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.io.FileInputStream
@ -102,6 +105,16 @@ fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFil
KhmParser.load(it, ctx)
} },
) { Text(text = "Load .khm") }
Button(
onClick = {
filePath?.let {
coroutineScope.launch(Dispatchers.IO) {
SasSQLiteParser.processZip(it, vm.repository, ctx)
coroutineScope.launch(Dispatchers.Main) { Toast.makeText(ctx, "File parsed.", Toast.LENGTH_LONG).show() }
}
}
}
) { Text("Load SAS") }
}
}
}
@ -129,7 +142,7 @@ suspend fun processZip(filePath: String?): List<Tile> = coroutineScope {
fun processEntry(zis: ZipInputStream, entry: ZipEntry): Tile {
val ba = ByteArray(entry.size.toInt())
var offset = 0
var size = zis.read(ba, 0, ba.size)
var size = zis.read(ba)
while (size > 0) {
offset += size
size = zis.read(ba, offset, ba.size - offset)

View file

@ -0,0 +1,82 @@
package com.mirenkov.ktheightmap.parser
import android.content.Context
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import android.util.Log
import com.mirenkov.ktheightmap.TAG
import com.mirenkov.ktheightmap.Tile
import com.mirenkov.ktheightmap.TileRepository
import com.mirenkov.ktheightmap.toBase64
import java.io.FileInputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipInputStream
// Parser for zipped sas.planet SQLite
class SasSQLiteParser {
companion object {
private const val TEMP_DB_NAME: String = "temp.db"
fun processZip(filePath: String, repo: TileRepository, ctx: Context) {
ZipInputStream(FileInputStream(filePath)).use { zis ->
var entry = zis.nextEntry
while(true) {
if (entry.name.endsWith(".sqlitedb")) {
processSqlite(zis, entry, repo, ctx)
}
entry = zis.nextEntry
if (entry == null)
break
}
}
}
private fun processSqlite(zis: ZipInputStream, entry: ZipEntry, repo: TileRepository, ctx: Context) {
val ba = ByteArray(entry.size.toInt())
var offset = 0
var size = zis.read(ba)
while (size > 0) {
offset += size
size = zis.read(ba, offset, ba.size - offset)
}
Log.i(TAG, entry.name)
Log.i(TAG, entry.size.toString())
Log.i(TAG, entry.compressedSize.toString())
val level = Regex("(?<=z)(\\d+)").find(entry.name)!!.value.toInt()
ctx.openFileOutput(TEMP_DB_NAME, Context.MODE_PRIVATE).use {
it.write(ba)
}
val path = ctx.getFileStreamPath(TEMP_DB_NAME).path
val db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.OPEN_READONLY)
val columns: Array<String> = arrayOf( "x", "y", "b" )
db.query("t", columns, null, null, null, null, null, null).use {
assert(it.moveToFirst())
assert(it.getType(0) == Cursor.FIELD_TYPE_INTEGER)
assert(it.getType(1) == Cursor.FIELD_TYPE_INTEGER)
assert(it.getType(2) == Cursor.FIELD_TYPE_BLOB)
processTile(it, repo, level)
while(it.moveToNext()) {
processTile(it, repo, level)
}
}
db.close()
}
private fun processTile(it: Cursor, repo: TileRepository, level: Int) {
val x = it.getInt(0)
val y = it.getInt(1)
val blob = it.getBlob(2)
val base64 = blob.toBase64()
val tile = Tile(x, y, level, base64)
val checkedTile = repo.getTile(x, y, level)
if (checkedTile == null)
repo.pushTile(tile)
}
}
}