SASPlanet sqlite parser
This commit is contained in:
parent
d1bf25b9c9
commit
516455209e
4 changed files with 98 additions and 2 deletions
2
TODO
2
TODO
|
|
@ -1,5 +1,5 @@
|
||||||
functionality:
|
functionality:
|
||||||
sasplanet zipped sqlitedbs parser
|
(done) sasplanet zipped sqlitedbs parser
|
||||||
refactor:
|
refactor:
|
||||||
extract file opener from settings activity
|
extract file opener from settings activity
|
||||||
extract zip parser from settings activity
|
extract zip parser from settings activity
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ class KhmParser {
|
||||||
ctx.openFileOutput(HEIGHT_FILE, Context.MODE_PRIVATE).use {
|
ctx.openFileOutput(HEIGHT_FILE, Context.MODE_PRIVATE).use {
|
||||||
it.write(inp.readBytes())
|
it.write(inp.readBytes())
|
||||||
}
|
}
|
||||||
|
inp.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
|
private fun getOffset(header: HeightInfo, x: Int, y: Int): Int {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package com.mirenkov.ktheightmap
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
|
|
@ -22,10 +23,12 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
import androidx.lifecycle.viewmodel.compose.LocalViewModelStoreOwner
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.mirenkov.ktheightmap.parser.SasSQLiteParser
|
||||||
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
|
import com.mirenkov.ktheightmap.ui.theme.KtHeightMapTheme
|
||||||
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
|
import com.nareshchocha.filepickerlibrary.FilePickerResultContracts
|
||||||
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
|
import com.nareshchocha.filepickerlibrary.models.DocumentFilePickerConfig
|
||||||
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
|
import com.nareshchocha.filepickerlibrary.models.FilePickerResult
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.io.FileInputStream
|
import java.io.FileInputStream
|
||||||
|
|
@ -102,6 +105,16 @@ fun SettingsMain(vm: TileViewModel, launcher: ActivityResultLauncher<DocumentFil
|
||||||
KhmParser.load(it, ctx)
|
KhmParser.load(it, ctx)
|
||||||
} },
|
} },
|
||||||
) { Text(text = "Load .khm") }
|
) { 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 {
|
fun processEntry(zis: ZipInputStream, entry: ZipEntry): Tile {
|
||||||
val ba = ByteArray(entry.size.toInt())
|
val ba = ByteArray(entry.size.toInt())
|
||||||
var offset = 0
|
var offset = 0
|
||||||
var size = zis.read(ba, 0, ba.size)
|
var size = zis.read(ba)
|
||||||
while (size > 0) {
|
while (size > 0) {
|
||||||
offset += size
|
offset += size
|
||||||
size = zis.read(ba, offset, ba.size - offset)
|
size = zis.read(ba, offset, ba.size - offset)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue