~repos /only-bible-app

#kotlin#android#ios

GIT_CONFIG_PARAMETERS="'http.version=HTTP/1.1'" git clone https://git.pyrossh.dev/only-bible-app.git
Discussions: https://groups.google.com/g/rust-embed-devs

The only bible app you will ever need. No ads. No in-app purchases. No distractions.



scripts/generate_flatbuffers.dart



import "dart:convert";
import "dart:io";
import "dart:typed_data";
import "package:flat_buffers/flat_buffers.dart" as fb;
import "package:only_bible_app/bible_data.dart";
// Converts bible .txt to FlatBuffer binary
// Format: bookName|bookIndex|chapterIndex|verseNo|heading|headingReferences|text...
Uint8List convertBibleToFlatBuffer(BibleItem item, String text) {
final books = <int, Map<int, List<Map<String, dynamic>>>>{};
final bookNames = <int, String>{};
final lines = text.split("\n");
for (var i = 0; i < lines.length; i++) {
final line = lines[i];
if (line.isEmpty) continue;
final arr = line.split("|");
// bookName|bookIndex|chapterIndex|verseNo|heading|headingReferences|text...
final bookName = arr[0];
final book = int.parse(arr[1]);
final chapter = int.parse(arr[2]);
final verseNo = int.parse(arr[3]);
final heading = arr[4];
final headingReferences = arr[5];
final verseText = arr.sublist(6).join("|");
bookNames.putIfAbsent(book, () => bookName);
books.putIfAbsent(book, () => {});
books[book]!.putIfAbsent(chapter, () => []);
books[book]![chapter]!.add({
"index": verseNo,
"book": book,
"chapter": chapter,
"heading": heading,
"heading_references": headingReferences,
"text": verseText,
});
}
final rfPattern = RegExp(r"RF:(\d+):(\d+):(\d+)");
// Build FlatBuffer
final builder = fb.Builder(initialSize: 1024 * 1024);
final bookOffsets = <int>[];
final sortedBooks = books.keys.toList()..sort();
for (final bookIdx in sortedBooks) {
final chapters = books[bookIdx]!;
final chapterOffsets = <int>[];
final sortedChapters = chapters.keys.toList()..sort();
for (final chapterIdx in sortedChapters) {
final verses = chapters[chapterIdx]!;
final verseOffsets = <int>[];
for (final v in verses) {
final headingOffset = builder.writeString(v["heading"] as String);
final textOffset = builder.writeString(v["text"] as String);
// Parse heading_references string into Reference tables
final refStr = v["heading_references"] as String;
int? refsVectorOffset;
if (refStr.isNotEmpty) {
final refOffsets = <int>[];
for (final match in rfPattern.allMatches(refStr)) {
builder.startTable(3);
builder.addInt32(0, int.parse(match.group(1)!));
builder.addInt32(1, int.parse(match.group(2)!));
builder.addInt32(2, int.parse(match.group(3)!));
refOffsets.add(builder.endTable());
}
if (refOffsets.isNotEmpty) {
refsVectorOffset = builder.writeList(refOffsets);
}
}
builder.startTable(6);
builder.addInt32(0, v["index"] as int);
builder.addInt32(1, v["book"] as int);
builder.addInt32(2, v["chapter"] as int);
builder.addOffset(3, headingOffset);
if (refsVectorOffset != null) {
builder.addOffset(4, refsVectorOffset);
}
builder.addOffset(5, textOffset);
verseOffsets.add(builder.endTable());
}
final versesVectorOffset = builder.writeList(verseOffsets);
builder.startTable(3);
builder.addInt32(0, chapterIdx);
builder.addInt32(1, bookIdx);
builder.addOffset(2, versesVectorOffset);
chapterOffsets.add(builder.endTable());
}
final chaptersVectorOffset = builder.writeList(chapterOffsets);
final bookNameOffset = builder.writeString(bookNames[bookIdx]!);
builder.startTable(3);
builder.addInt32(0, bookIdx);
builder.addOffset(1, bookNameOffset);
builder.addOffset(2, chaptersVectorOffset);
bookOffsets.add(builder.endTable());
}
final booksVectorOffset = builder.writeList(bookOffsets);
final nameOffset = builder.writeString(item.fileName);
final languageCodeOffset = builder.writeString(item.languageCode);
final languageEnglishOffset = builder.writeString(item.languageEnglish);
final languageNativeOffset = builder.writeString(item.languageNative);
final voiceNameOffset = builder.writeString(item.voiceName);
final oldTestamentTitleOffset = builder.writeString(item.oldTestamentTitle);
final newTestamentTitleOffset = builder.writeString(item.newTestamentTitle);
final bibleSelectTitleOffset = builder.writeString(item.bibleSelectTitle);
final themeTitleOffset = builder.writeString(item.themeTitle);
final boldFontTitleOffset = builder.writeString(item.boldFontTitle);
final engTitlesOffset = builder.writeString(item.engTitles);
final settingsTitleOffset = builder.writeString(item.settingsTitle);
builder.startTable(13);
builder.addOffset(0, nameOffset);
builder.addOffset(1, languageCodeOffset);
builder.addOffset(2, languageEnglishOffset);
builder.addOffset(3, languageNativeOffset);
builder.addOffset(4, voiceNameOffset);
builder.addOffset(5, oldTestamentTitleOffset);
builder.addOffset(6, newTestamentTitleOffset);
builder.addOffset(7, bibleSelectTitleOffset);
builder.addOffset(8, themeTitleOffset);
builder.addOffset(9, boldFontTitleOffset);
builder.addOffset(10, engTitlesOffset);
builder.addOffset(11, settingsTitleOffset);
builder.addOffset(12, booksVectorOffset);
final bibleOffset = builder.endTable();
builder.finish(bibleOffset);
return builder.buffer;
}
void main() async {
final biblesDir = Directory("scripts/files");
final txtFiles = biblesDir.listSync().whereType<File>().where((f) => f.path.endsWith(".txt"));
for (final file in txtFiles) {
final fileName = file.uri.pathSegments.last.replaceAll(".txt", "");
final item = bibleItems[fileName];
if (item == null) {
print("Skipping $fileName: no matching BibleItem");
continue;
}
final text = await file.readAsString(encoding: utf8);
final bytes = convertBibleToFlatBuffer(item, text);
final compressed = gzip.encode(bytes);
final outPath = "assets/bibles/$fileName.bin.gz";
await File(outPath).writeAsBytes(compressed);
final txtSize = (file.lengthSync() / 1024).toStringAsFixed(1);
final binSize = (compressed.length / 1024).toStringAsFixed(1);
print("$fileName: ${txtSize}KB txt -> ${binSize}KB bin");
}
print("Done! Generated ${txtFiles.length} .bin files.");
}