diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-hdpi/ic_launcher.png b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-hdpi/ic_launcher.png
index 64e3de940..66b158ae8 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-hdpi/ic_launcher.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-mdpi/ic_launcher.png b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-mdpi/ic_launcher.png
index a09c04f57..ebdb3ba56 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-mdpi/ic_launcher.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xhdpi/ic_launcher.png b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xhdpi/ic_launcher.png
index 8e2e2aa13..10e8972dd 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xhdpi/ic_launcher.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxhdpi/ic_launcher.png b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxhdpi/ic_launcher.png
index 88ccd2622..96d8d433e 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxhdpi/ic_launcher.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxxhdpi/ic_launcher.png b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxxhdpi/ic_launcher.png
index bf3a29b65..1790fcb89 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxxhdpi/ic_launcher.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/res/web_hi_res_512.png b/multisrc/overrides/comicgamma/webcomicgamma/res/web_hi_res_512.png
index 620440395..d2a131003 100644
Binary files a/multisrc/overrides/comicgamma/webcomicgamma/res/web_hi_res_512.png and b/multisrc/overrides/comicgamma/webcomicgamma/res/web_hi_res_512.png differ
diff --git a/multisrc/overrides/comicgamma/webcomicgamma/src/WebComicGamma.kt b/multisrc/overrides/comicgamma/webcomicgamma/src/WebComicGamma.kt
new file mode 100644
index 000000000..f6335e394
--- /dev/null
+++ b/multisrc/overrides/comicgamma/webcomicgamma/src/WebComicGamma.kt
@@ -0,0 +1,83 @@
+package eu.kanade.tachiyomi.extension.ja.webcomicgamma
+
+import eu.kanade.tachiyomi.multisrc.comicgamma.ComicGamma
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import org.jsoup.select.Evaluator
+
+class WebComicGamma : ComicGamma("Web Comic Gamma", "https://webcomicgamma.takeshobo.co.jp") {
+    override val supportsLatest = false
+
+    override fun popularMangaSelector() = ".tab_panel.active .manga_item"
+    override fun popularMangaFromElement(element: Element) = SManga.create().apply {
+        url = element.selectFirst(Evaluator.Tag("a")).attr("href")
+        title = element.selectFirst(Evaluator.Class("manga_title")).text()
+        author = element.selectFirst(Evaluator.Class("manga_author")).text()
+        val genreList = element.select(Evaluator.Tag("li")).map { it.text() }
+        genre = genreList.joinToString()
+        status = when {
+            genreList.contains("完結") && !genreList.contains("リピート配信") -> SManga.COMPLETED
+            else -> SManga.ONGOING
+        }
+        thumbnail_url = element.selectFirst(Evaluator.Tag("img")).absUrl("src")
+    }
+
+    override fun latestUpdatesRequest(page: Int) = throw UnsupportedOperationException("Not used.")
+    override fun latestUpdatesNextPageSelector() = throw UnsupportedOperationException("Not used.")
+    override fun latestUpdatesSelector() = throw UnsupportedOperationException("Not used.")
+    override fun latestUpdatesFromElement(element: Element) = throw UnsupportedOperationException("Not used.")
+
+    override fun mangaDetailsParse(document: Document): SManga {
+        val titleElement = document.selectFirst(Evaluator.Class("manga__title"))
+        val titleName = titleElement.child(0).text()
+        val desc = document.selectFirst(".detail__item > p")?.run {
+            select(Evaluator.Tag("br")).prepend("\\n")
+            this.text().replace("\\n", "\n").replace("\n ", "\n")
+        }
+        val listResponse = client.newCall(popularMangaRequest(0)).execute()
+        val manga = popularMangaParse(listResponse).mangas.find { it.title == titleName }
+        return manga?.apply { description = desc } ?: SManga.create().apply {
+            author = titleElement.child(1).text()
+            description = desc
+            status = SManga.UNKNOWN
+            val slug = document.location().removeSuffix("/").substringAfterLast("/")
+            thumbnail_url = "$baseUrl/img/manga_thumb/${slug}_list.jpg"
+        }
+    }
+
+    override fun chapterListSelector() = ".read__area > .read__outer > a"
+    override fun chapterFromElement(element: Element) = SChapter.create().apply {
+        url = element.attr("href").toOldChapterUrl()
+        val number = url.removeSuffix("/").substringAfterLast('/').replace('_', '.')
+        val list = element.selectFirst(Evaluator.Class("read__contents")).children()
+        name = "[$number] ${list[0].text()}"
+        if (list.size >= 3) {
+            date_upload = dateFormat.parse(list[2].text())?.time ?: 0L
+        }
+        if (date_upload <= 0L) date_upload = -1L // hide unknown ones
+    }
+
+    override fun pageListRequest(chapter: SChapter) =
+        GET(baseUrl + chapter.url.toNewChapterUrl(), headers)
+
+    companion object {
+        private val dateFormat by lazy { getJSTFormat("yyyy年M月dd日") }
+
+        private fun String.toOldChapterUrl(): String {
+            // ../../../_files/madeinabyss/063_2/
+            val segments = split('/')
+            val size = segments.size
+            val slug = segments[size - 3]
+            val number = segments[size - 2]
+            return "/manga/$slug/_files/$number/"
+        }
+
+        private fun String.toNewChapterUrl(): String {
+            val segments = split('/')
+            return "/_files/${segments[2]}/${segments[4]}/"
+        }
+    }
+}
diff --git a/multisrc/overrides/comicgamma/webcomicgammaplus/src/WebComicGammaPlus.kt b/multisrc/overrides/comicgamma/webcomicgammaplus/src/WebComicGammaPlus.kt
new file mode 100644
index 000000000..f8d1d55a8
--- /dev/null
+++ b/multisrc/overrides/comicgamma/webcomicgammaplus/src/WebComicGammaPlus.kt
@@ -0,0 +1,77 @@
+package eu.kanade.tachiyomi.extension.ja.webcomicgammaplus
+
+import eu.kanade.tachiyomi.multisrc.comicgamma.ComicGamma
+import eu.kanade.tachiyomi.network.GET
+import eu.kanade.tachiyomi.source.model.SChapter
+import eu.kanade.tachiyomi.source.model.SManga
+import org.jsoup.nodes.Document
+import org.jsoup.nodes.Element
+import java.text.DateFormat
+
+class WebComicGammaPlus : ComicGamma("Web Comic Gamma Plus", "https://gammaplus.takeshobo.co.jp") {
+    override val supportsLatest = true
+
+    override fun popularMangaSelector() = ".work_list li"
+    override fun popularMangaFromElement(element: Element) = SManga.create().apply {
+        setUrlWithoutDomain(element.select("a").attr("abs:href"))
+        val image = element.select("img")
+        title = image.attr("alt").removePrefix("『").removeSuffix("』作品ページへ")
+        thumbnail_url = image.attr("abs:src")
+    }
+
+    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/", headers)
+    override fun latestUpdatesNextPageSelector(): String? = null
+    override fun latestUpdatesSelector() = ".whatsnew li:contains(読む)"
+    override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
+        val url = element.select(".show_detail").attr("abs:href")
+        setUrlWithoutDomain(url)
+        title = element.select("h3").textNodes()[0].text()
+        thumbnail_url = url + "main.jpg"
+    }
+
+    override fun mangaDetailsParse(document: Document) = SManga.create().apply {
+        title = document.select("h1").text()
+        author = document.select(".author").text()
+        description = document.select(".work_sammary").text()
+        val nextDate = document.select(".episode_caption:contains(【次回更新】)")
+        if (nextDate.isNotEmpty()) {
+            val dateStr = nextDate.textNodes()
+                .filter { it.text().contains("【次回更新】") }[0]
+                .text().trim().removePrefix("【次回更新】")
+            val localDate = JST_FORMAT_DESC.parseJST(dateStr)?.let { LOCAL_FORMAT_DESC.format(it) }
+            if (localDate != null) description = "【Next/Repeat: $localDate】\n$description"
+        }
+    }
+
+    override fun chapterListSelector() =
+        ".box_episode > .box_episode_L:contains(読む), .box_episode > .box_episode_M:contains(読む)" // filter out purchase links
+
+    override fun chapterFromElement(element: Element) = SChapter.create().apply {
+        val url = element.select("a[id^=read]").attr("abs:href")
+        setUrlWithoutDomain(url)
+        val chapterNumber = url.removeSuffix("/").substringAfterLast('/').replace('_', '.')
+        val title = element.select(".episode_title").textNodes().filterNot {
+            it.text().contains("集中連載") || it.text().contains("配信中!!")
+        }.joinToString("/") { it.text() }
+        name = "$chapterNumber $title"
+        element.select(".episode_caption").textNodes().forEach {
+            if (it.text().contains("【公開日】")) {
+                val date = it.text().trim().removePrefix("【公開日】")
+                date_upload = JST_FORMAT_LIST.parseJST(date)!!.time
+            } else if (it.text().contains("【次回更新】")) {
+                val date = it.text().trim().removePrefix("【次回更新】")
+                val localDate = JST_FORMAT_LIST.parseJST(date)?.let { LOCAL_FORMAT_LIST.format(it) }
+                if (localDate != null) scanlator = "~$localDate"
+            }
+        }
+        if (date_upload <= 0L) date_upload = -1L // hide unknown ones
+    }
+
+    companion object {
+        private const val datePattern = "yyyy年M月dd日(E)"
+        private val JST_FORMAT_DESC by lazy { getJSTFormat(datePattern) }
+        private val JST_FORMAT_LIST by lazy { getJSTFormat(datePattern) }
+        private val LOCAL_FORMAT_DESC by lazy { DateFormat.getDateTimeInstance() }
+        private val LOCAL_FORMAT_LIST by lazy { DateFormat.getDateTimeInstance() }
+    }
+}
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGamma.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGamma.kt
index 16ff2dee8..abd3a641d 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGamma.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGamma.kt
@@ -4,46 +4,24 @@ import eu.kanade.tachiyomi.network.GET
 import eu.kanade.tachiyomi.source.model.FilterList
 import eu.kanade.tachiyomi.source.model.MangasPage
 import eu.kanade.tachiyomi.source.model.Page
-import eu.kanade.tachiyomi.source.model.SChapter
-import eu.kanade.tachiyomi.source.model.SManga
 import eu.kanade.tachiyomi.source.online.ParsedHttpSource
 import org.jsoup.nodes.Document
 import org.jsoup.nodes.Element
 import rx.Observable
-import java.text.DateFormat.getDateTimeInstance
 import java.text.SimpleDateFormat
 import java.util.Locale
 import java.util.TimeZone
 
-open class ComicGamma(
+abstract class ComicGamma(
     override val name: String,
     override val baseUrl: String,
     override val lang: String = "ja",
 ) : ParsedHttpSource() {
-    override val supportsLatest = true
 
     override val client = network.client.newBuilder().addInterceptor(PtImgInterceptor).build()
 
     override fun popularMangaRequest(page: Int) = GET("$baseUrl/manga/", headers)
     override fun popularMangaNextPageSelector(): String? = null
-    override fun popularMangaSelector() = ".work_list li"
-    override fun popularMangaFromElement(element: Element) = SManga.create().apply {
-        setUrlWithoutDomain(element.select("a").attr("abs:href"))
-        val image = element.select("img")
-        title = image.attr("alt").removePrefix("『").removeSuffix("』作品ページへ")
-        thumbnail_url = image.attr("abs:src")
-    }
-
-    override fun latestUpdatesRequest(page: Int) = GET("$baseUrl/", headers)
-    override fun latestUpdatesNextPageSelector(): String? = null
-    override fun latestUpdatesSelector() = ".whatsnew li:contains(読む)"
-
-    override fun latestUpdatesFromElement(element: Element) = SManga.create().apply {
-        val url = element.select(".show_detail").attr("abs:href")
-        setUrlWithoutDomain(url)
-        title = element.select("h3").textNodes()[0].text()
-        thumbnail_url = url + "main.jpg"
-    }
 
     override fun fetchSearchManga(page: Int, query: String, filters: FilterList): Observable<MangasPage> =
         fetchPopularManga(page).map { p -> MangasPage(p.mangas.filter { it.title.contains(query) }, false) }
@@ -54,44 +32,6 @@ open class ComicGamma(
     override fun searchMangaRequest(page: Int, query: String, filters: FilterList) =
         throw UnsupportedOperationException("Not used.")
 
-    override fun mangaDetailsParse(document: Document) = SManga.create().apply {
-        title = document.select("h1").text()
-        author = document.select(".author").text()
-        description = document.select(".work_sammary").text()
-        val nextDate = document.select(".episode_caption:contains(【次回更新】)")
-        if (nextDate.isNotEmpty()) {
-            val dateStr = nextDate.textNodes()
-                .filter { it.text().contains("【次回更新】") }[0]
-                .text().trim().removePrefix("【次回更新】")
-            val localDate = JST_FORMAT_DESC.parseJST(dateStr)?.let { LOCAL_FORMAT_DESC.format(it) }
-            if (localDate != null) description = "【Next/Repeat: $localDate】\n$description"
-        }
-    }
-
-    override fun chapterListSelector() =
-        ".box_episode > .box_episode_L:contains(読む), .box_episode > .box_episode_M:contains(読む)" // filter out purchase links
-
-    override fun chapterFromElement(element: Element) = SChapter.create().apply {
-        val url = element.select("a[id^=read]").attr("abs:href")
-        setUrlWithoutDomain(url)
-        val chapterNumber = url.removeSuffix("/").substringAfterLast('/').replace('_', '.')
-        val title = element.select(".episode_title").textNodes().filterNot {
-            it.text().contains("集中連載") || it.text().contains("配信中!!")
-        }.joinToString("/") { it.text() }
-        name = "$chapterNumber $title"
-        element.select(".episode_caption").textNodes().forEach {
-            if (it.text().contains("【公開日】")) {
-                val date = it.text().trim().removePrefix("【公開日】")
-                date_upload = JST_FORMAT_LIST.parseJST(date)!!.time
-            } else if (it.text().contains("【次回更新】")) {
-                val date = it.text().trim().removePrefix("【次回更新】")
-                val localDate = JST_FORMAT_LIST.parseJST(date)?.let { LOCAL_FORMAT_LIST.format(it) }
-                if (localDate != null) scanlator = "~$localDate"
-            }
-        }
-        if (date_upload <= 0L) date_upload = -1L // hide unknown ones
-    }
-
     override fun pageListParse(document: Document) =
         document.select("#content > div[data-ptimg]").mapIndexed { i, e ->
             Page(i, imageUrl = e.attr("abs:data-ptimg"))
@@ -99,18 +39,14 @@ open class ComicGamma(
 
     override fun imageUrlParse(document: Document) = throw UnsupportedOperationException("Not used.")
 
-    // for thread-safety
-    private val JST_FORMAT_DESC = getJSTFormat()
-    private val JST_FORMAT_LIST = getJSTFormat()
-    private val LOCAL_FORMAT_DESC = getDateTimeInstance()
-    private val LOCAL_FORMAT_LIST = getDateTimeInstance()
-
-    private fun SimpleDateFormat.parseJST(date: String) = parse(date)?.apply {
-        time += 12 * 3600 * 1000 // updates at 12 noon
-    }
-
-    private fun getJSTFormat() =
-        SimpleDateFormat("yyyy年M月dd日(E)", Locale.JAPANESE).apply {
-            timeZone = TimeZone.getTimeZone("GMT+09:00")
+    companion object {
+        internal fun SimpleDateFormat.parseJST(date: String) = parse(date)?.apply {
+            time += 12 * 3600 * 1000 // updates at 12 noon
         }
+
+        internal fun getJSTFormat(datePattern: String) =
+            SimpleDateFormat(datePattern, Locale.JAPANESE).apply {
+                timeZone = TimeZone.getTimeZone("GMT+09:00")
+            }
+    }
 }
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGammaGenerator.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGammaGenerator.kt
index 79242b8ed..cef2f9461 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGammaGenerator.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/ComicGammaGenerator.kt
@@ -16,7 +16,7 @@ class ComicGammaGenerator : ThemeSourceGenerator {
             className = "WebComicGamma",
             pkgName = "webcomicgamma",
             sourceName = "Web Comic Gamma",
-            overrideVersionCode = 0,
+            overrideVersionCode = 1,
         ),
         SingleLang(
             name = "Web Comic Gamma Plus",
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImg.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImg.kt
index d3f2c1969..055b73e0b 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImg.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImg.kt
@@ -5,24 +5,22 @@ import kotlinx.serialization.Serializable
 val COORD_REGEX = Regex("""^i:(\d+),(\d+)\+(\d+),(\d+)>(\d+),(\d+)$""")
 
 @Serializable
-data class PtImg(val resources: Resource, val views: List<View>) {
+class PtImg(val resources: Resource, val views: List<View>) {
     fun getFilename() = resources.i.src
     fun getViewSize() = Pair(views[0].width, views[0].height)
-    fun getTranslations() = views[0].coords.map(::toTranslation)
-
-    private fun toTranslation(coord: String): Translation {
-        val v = COORD_REGEX.matchEntire(coord)!!.destructured.toList().map(String::toInt)
-        return Translation(v[0], v[1], v[2], v[3], v[4], v[5])
+    fun getTranslations() = views[0].coords.map { coord ->
+        val v = COORD_REGEX.matchEntire(coord)!!.groupValues.drop(1).map { it.toInt() }
+        Translation(v[0], v[1], v[2], v[3], v[4], v[5])
     }
 }
 
 @Serializable
-data class Resource(val i: Image)
+class Resource(val i: Image)
 
 @Serializable
-data class Image(val src: String, val width: Int, val height: Int)
+class Image(val src: String, val width: Int, val height: Int)
 
 @Serializable
-data class View(val width: Int, val height: Int, val coords: List<String>)
+class View(val width: Int, val height: Int, val coords: List<String>)
 
-data class Translation(val ix: Int, val iy: Int, val w: Int, val h: Int, val vx: Int, val vy: Int)
+class Translation(val ix: Int, val iy: Int, val w: Int, val h: Int, val vx: Int, val vy: Int)
diff --git a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImgInterceptor.kt b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImgInterceptor.kt
index 230993869..a006809b0 100644
--- a/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImgInterceptor.kt
+++ b/multisrc/src/main/java/eu/kanade/tachiyomi/multisrc/comicgamma/PtImgInterceptor.kt
@@ -20,12 +20,13 @@ object PtImgInterceptor : Interceptor {
     override fun intercept(chain: Interceptor.Chain): Response {
         val request = chain.request()
         val response = chain.proceed(request)
-        val url = request.url.toString()
-        if (!url.endsWith(".ptimg.json")) return response
+        val url = request.url
+        val path = url.pathSegments
+        if (!path.last().endsWith(".ptimg.json")) return response
 
         val metadata = json.decodeFromString<PtImg>(response.body!!.string())
-        val imgRequest = request.newBuilder()
-            .url(url.replaceAfterLast('/', metadata.getFilename())).build()
+        val imageUrl = url.newBuilder().setEncodedPathSegment(path.size - 1, metadata.getFilename()).build()
+        val imgRequest = request.newBuilder().url(imageUrl).build()
         val imgResponse = chain.proceed(imgRequest)
         val image = BitmapFactory.decodeStream(imgResponse.body!!.byteStream())
         val (width, height) = metadata.getViewSize()