Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,57 @@ All notable changes to mobileGF2logger are documented here.

## Unreleased

## 2.1.2 - 2026-08-09

### Changed

- Re-center the launcher emblem against the real source canvas, reduce it by
10%, and brighten the matte silver and gold-orange finish without redrawing
its silhouette or introducing resampling artifacts.
- Make the English project summary more descriptive for people searching for a
GIRLS' FRONTLINE 2 or GF2 Platoon management companion.

### Fixed

- Bound parsed-packet storage and table rendering by input size, row count,
column count, cell count, and cell length so hostile or corrupt payloads
cannot create unbounded Android view trees.
- Bound multi-part protocol continuations by byte and fragment counts, discard
an oversized pending dataset, quarantine every remaining fragment through its
terminal frame before decoding it, and enforce the byte cap from the first
continuation. A malformed terminal fragment now also clears quarantine so a
later legitimate payload of the same type is not discarded.
- Bound roster imports, per-payload activity observations, retained activity
history, and unresolved-name reconciliation. Failed or duplicate roster
imports now roll back their newly retained evidence as one operation.
- Apply the retained-activity cap after both activity packets and Updates-
derived Daily Patrol facts, without waiting for a database reopen.
- Neutralize spreadsheet formulas in every captured roster, activity, Updates,
and formation packet table as well as all spreadsheet-facing CSV exports,
while preserving internal roster evidence losslessly.
- Upgrade schema-v6/v7 databases directly through the v10 activity-table
rebuild without attempting to recreate already-installed identity indexes.
- Reconcile an observed roster absence against an exact or annotated open
membership period, preserving its strong boundary and note while creating a
later rejoin period instead of suppressing the gap. Annotated weak boundaries
can still expand when late roster evidence proves a wider presence interval.
- Preserve an imported roster's parsed capture time so exporting the latest
Platoon CSV cannot accidentally select a newly imported historical file.
- Roll back all nested SQLite reconciliation writes together with newly
retained roster files when a multi-file import fails.
- Rotate bounded unresolved-activity reconciliation through the complete
retained backlog so an unmatchable recent batch cannot starve older evidence,
backed by a global capture-time retention index.
- Enforce roster member-count and name-length limits at live-capture writing,
repository, and SQLite boundaries as well as during user-selected CSV import.
- Version the new retention index and maintenance cursor as schema v11 so v10
backups migrate through the same strict current-schema validation path.
- Break equal capture-time ties by retained evidence filename when selecting
the latest Platoon CSV for export.
- Accept a capped pre-Gunsmoke opening anchor only when it belongs to the
immediately preceding Monday-through-Saturday counter period, using the
configured game-day timezone across daylight-saving transitions.

## 2.1.1 - 2026-08-08

### Added
Expand Down
9 changes: 6 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
# mobileGF2logger
# mobileGF2logger — GIRLS' FRONTLINE 2 (GF2) Platoon Manager

[![Android](https://github.com/1window2/mobileGF2logger/actions/workflows/android.yml/badge.svg)](https://github.com/1window2/mobileGF2logger/actions/workflows/android.yml)
[![CodeQL](https://github.com/1window2/mobileGF2logger/actions/workflows/codeql.yml/badge.svg)](https://github.com/1window2/mobileGF2logger/actions/workflows/codeql.yml)
[![Dependabot](https://img.shields.io/github/issues-pr/1window2/mobileGF2logger/dependencies?label=Dependabot&logo=dependabot)](https://github.com/1window2/mobileGF2logger/pulls?q=is%3Apr+is%3Aopen+author%3Aapp%2Fdependabot)

한국어로 된 설명은 [여기](README_KR.md)에서 보실 수 있습니다.

This is a **Platoon(서클)** management tool for the Android client of [GIRLS' FRONTLINE 2: EXILIUM](https://gf2exilium.sunborngame.com/). It is Android-only; an iOS release is not planned.
This is a **GIRLS' FRONTLINE 2: EXILIUM (GF2) Platoon/서클 management and logging companion** for the [Android game client](https://gf2exilium.sunborngame.com/). It is Android-only; an iOS release is not planned.

mobileGF2logger is a lightweight, non-root app for Platoon masters. It uses Android's per-app VPN permission to parse supported plaintext server responses entirely on the phone without storing raw traffic.
mobileGF2logger is a lightweight, non-root GF2 logger for Platoon masters. It
turns supported roster and activity responses into member history and weekly
task tables entirely on the phone. Android's per-app VPN permission limits
capture to the selected game package, and raw traffic is never stored.

## Features

Expand Down
4 changes: 2 additions & 2 deletions README_KR.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# mobileGF2logger
# mobileGF2logger — 소녀전선 2(GF2) 서클 관리 도구

[![Android](https://github.com/1window2/mobileGF2logger/actions/workflows/android.yml/badge.svg)](https://github.com/1window2/mobileGF2logger/actions/workflows/android.yml)
[![CodeQL](https://github.com/1window2/mobileGF2logger/actions/workflows/codeql.yml/badge.svg)](https://github.com/1window2/mobileGF2logger/actions/workflows/codeql.yml)
[![Dependabot](https://img.shields.io/github/issues-pr/1window2/mobileGF2logger/dependencies?label=Dependabot&logo=dependabot)](https://github.com/1window2/mobileGF2logger/pulls?q=is%3Apr+is%3Aopen+author%3Aapp%2Fdependabot)

이 프로젝트는 [GIRLS' FRONTLINE 2: EXILIUM](https://gf2exilium.sunborngame.com/) Android 클라이언트용 **서클(Platoon)** 관리 도구입니다. Android만 지원하며 iOS 버전은 출시할 계획이 없습니다.
이 프로젝트는 [GIRLS' FRONTLINE 2: EXILIUM](https://gf2exilium.sunborngame.com/) (소녀전선 2, GF2) Android 클라이언트용 **서클(Platoon) 관리 및 기록 도구**입니다. Android만 지원하며 iOS 버전은 출시할 계획이 없습니다.

mobileGF2logger는 서클장을 위한 가벼운 비루팅 앱입니다. Android의 앱별 VPN 권한을 사용하여 지원되는 평문 서버 응답을 휴대전화 안에서 파싱하며, 원본 트래픽은 저장하지 않습니다.

Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,8 @@ android {
applicationId = 'dev.gf2log'
minSdk = 26
targetSdk = 36
versionCode = 20101
versionName = '2.1.1'
versionCode = 20102
versionName = '2.1.2'
testInstrumentationRunner = 'androidx.test.runner.AndroidJUnitRunner'

ndk {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dev.gf2log.app.management

import android.content.ContentValues
import android.content.Context
import android.database.sqlite.SQLiteDatabase
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import dev.gf2log.app.TargetPackagePreferences
Expand Down Expand Up @@ -80,6 +81,48 @@ class PlatoonBackupManagerIntegrationTest {
}
}

@Test
fun schemaTenCompleteBackupMigratesBeforeCurrentContractValidation() {
seedDatabase(ARCHIVED_UID, "Archived member", "archived-source.csv")
settingsStore.replace(archivedSettings())
val databaseFile = context.getDatabasePath(PlatoonSchema.DATABASE_NAME)
SQLiteDatabase.openDatabase(databaseFile.path, null, SQLiteDatabase.OPEN_READWRITE).use { legacy ->
legacy.execSQL("DROP INDEX platoon_activity_resolution_retention")
legacy.execSQL("DROP INDEX platoon_activity_retention_order")
legacy.execSQL("DROP TABLE platoon_maintenance_state")
legacy.version = 10
}
val archive = ByteArrayOutputStream().also { output ->
BackupArchive.write(
output,
databaseFile,
AppBackupSettingsCodec.encode(archivedSettings()),
)
}.toByteArray()

replaceDatabaseWithCurrentState()
settingsStore.replace(currentSettings())
PlatoonBackupManager(context).restoreFull(ByteArrayInputStream(archive))

assertEquals(archivedSettings(), settingsStore.read())
PlatoonDatabase(context).use { database ->
val restored = database.readableDatabase
assertEquals(PlatoonSchema.CURRENT_VERSION, restored.version)
assertEquals(1L, count(restored, "members", "uid = ?", ARCHIVED_UID))
assertEquals(
1L,
restored.rawQuery(
"SELECT COUNT(*) FROM sqlite_master " +
"WHERE type = 'table' AND name = 'platoon_maintenance_state'",
null,
).use { cursor ->
assertTrue(cursor.moveToFirst())
cursor.getLong(0)
},
)
}
}

@Test
fun platoonOnlyRestoreRetiresUnrelatedRetainedRosterEvidence() {
seedDatabase(ARCHIVED_UID, "Archived member", "archived-source.csv")
Expand Down
Loading
Loading