diff --git a/src/main/kotlin/io/github/sculk_cli/commands/AddByUrl.kt b/src/main/kotlin/io/github/sculk_cli/commands/AddByUrl.kt index 5f411fc..b5505a9 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/AddByUrl.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/AddByUrl.kt @@ -45,21 +45,18 @@ class AddByUrl : } val existingManifest = ctx.pack.getManifest("$dir/$transformedSlug.sculk.json") + val hashes = FileManifestHashes( + sha1 = contents.digestSha1(), + sha512 = contents.digestSha512(), + murmur2 = contents.digestMurmur2() + ) val fileManifest = if (existingManifest != null) { - if (existingManifest.hashes.sha1 != contents.digestSha1() || existingManifest.hashes.sha512 != contents.digestSha512()) { - error("File hashes do not match for $filename") - } - - existingManifest.sources.url = FileManifestUrlSource(url.toString()) + existingManifest.sources.url = FileManifestUrlSource(url.toString(), hashes) existingManifest } else { FileManifest( - filename = filename, side = side, hashes = FileManifestHashes( - sha1 = contents.digestSha1(), - sha512 = contents.digestSha512(), - murmur2 = contents.digestMurmur2() - ), fileSize = contents.size, sources = FileManifestSources( - curseforge = null, modrinth = null, url = FileManifestUrlSource(url.toString()) + filename = filename, side = side, fileSize = contents.size, sources = FileManifestSources( + curseforge = null, modrinth = null, url = FileManifestUrlSource(url.toString(), hashes) ) ) } diff --git a/src/main/kotlin/io/github/sculk_cli/commands/ExportModrinth.kt b/src/main/kotlin/io/github/sculk_cli/commands/ExportModrinth.kt index bed6fe3..1021f47 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/ExportModrinth.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/ExportModrinth.kt @@ -13,6 +13,7 @@ import io.github.sculk_cli.util.toModrinthEnvClientSupport import io.github.sculk_cli.util.toModrinthEnvServerSupport import io.github.sculk_cli.modrinth.* import io.github.sculk_cli.modrinth.models.ModrinthVersionFileHashes +import io.github.sculk_cli.pack.FileManifestHashes import java.io.File import java.nio.file.Paths @@ -42,13 +43,16 @@ class ExportModrinth : for ((path, fileManifest) in ctx.pack.getManifests().entries) { val downloadUrls = mutableListOf() + var hashes: FileManifestHashes? = null if (fileManifest.sources.modrinth != null) { downloadUrls += fileManifest.sources.modrinth!!.fileUrl + hashes = fileManifest.sources.modrinth!!.hashes } if (fileManifest.sources.url != null) { downloadUrls += fileManifest.sources.url!!.url + hashes = hashes ?: fileManifest.sources.url!!.hashes } val filteredDownloadUrls = downloadUrls @@ -61,11 +65,16 @@ class ExportModrinth : continue } + if (hashes == null) { + terminal.warning("File $path will not be included as it does not have hashes") + continue + } + files += ModrinthPackFile( path = File(path).resolveSibling(fileManifest.filename).toString(), hashes = ModrinthVersionFileHashes( - sha1 = fileManifest.hashes.sha1, - sha512 = fileManifest.hashes.sha512 + sha1 = hashes.sha1, + sha512 = hashes.sha512 ), env = ModrinthPackFileEnv( clientSupport = fileManifest.side.toModrinthEnvClientSupport(), diff --git a/src/main/kotlin/io/github/sculk_cli/commands/ImportCurseforge.kt b/src/main/kotlin/io/github/sculk_cli/commands/ImportCurseforge.kt index 5a358c8..6c19744 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/ImportCurseforge.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/ImportCurseforge.kt @@ -96,17 +96,17 @@ class ImportCurseforge : val fileManifest = FileManifest( filename = cfFile.fileName, side = cfFile.getSide().toSide(), - hashes = FileManifestHashes( - sha1 = tempFile.digestSha1(), - sha512 = tempFile.digestSha512(), - murmur2 = tempFile.digestMurmur2() - ), fileSize = tempFile.size, sources = FileManifestSources( curseforge = FileManifestCurseforgeSource( projectId = mod.id, fileId = file.fileId, - fileUrl = cfFile.downloadUrl!! + fileUrl = cfFile.downloadUrl!!, + hashes = FileManifestHashes( + sha1 = tempFile.digestSha1(), + sha512 = tempFile.digestSha512(), + murmur2 = tempFile.digestMurmur2() + ), ), modrinth = null, url = null diff --git a/src/main/kotlin/io/github/sculk_cli/commands/ImportModrinth.kt b/src/main/kotlin/io/github/sculk_cli/commands/ImportModrinth.kt index 6762ee8..dd5e775 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/ImportModrinth.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/ImportModrinth.kt @@ -35,6 +35,8 @@ import io.github.sculk_cli.util.toModLoader import io.github.sculk_cli.util.toSide import io.github.sculk_cli.modrinth.getLoaderVersionPair import io.github.sculk_cli.modrinth.importModrinthPack +import io.github.sculk_cli.util.digestSha1 +import io.github.sculk_cli.util.digestSha512 import java.io.File import java.nio.file.Paths import kotlin.collections.plusAssign @@ -75,11 +77,6 @@ class ImportModrinth : val fileManifest = FileManifest( filename = File(file.path).name, side = file.env?.toSide() ?: Side.Both, - hashes = FileManifestHashes( - sha1 = file.hashes.sha1, - sha512 = file.hashes.sha512, - murmur2 = -1, // Updated later in the loop - ), fileSize = file.fileSizeInBytes, sources = FileManifestSources( curseforge = null, @@ -93,14 +90,14 @@ class ImportModrinth : for (downloadUrl in file.downloadUrls) { progress.advance(1) progress.update { context = downloadUrl } - - if (fileManifest.hashes.murmur2 == -1L) { - val file = downloadFileTemp(parseUrl(downloadUrl)) - fileManifest.hashes.murmur2 = file.readBytes().digestMurmur2() - } + val file = downloadFileTemp(parseUrl(downloadUrl)) + val bytes = file.readBytes() + val sha1 = bytes.digestSha1() + val sha512 = bytes.digestSha512() + val murmur2 = bytes.digestMurmur2() if (parseUrl(downloadUrl).host == "cdn.modrinth.com") { - val version = ctx.modrinth.getVersionFromHash(file.hashes.sha1) + val version = ctx.modrinth.getVersionFromHash(sha1) ?: run { terminal.warning("File ${file.path} includes a Modrinth file that does not seem to exist") null @@ -108,12 +105,21 @@ class ImportModrinth : fileManifest.sources.modrinth = FileManifestModrinthSource( projectId = version.projectId, - fileUrl = downloadUrl + fileUrl = downloadUrl, + hashes = FileManifestHashes( + sha1 = sha1, + sha512 = sha512, + murmur2 = murmur2, + ), ) slug = ctx.modrinth.getProject(version.projectId)!!.slug } else if (fileManifest.sources.url == null) { - fileManifest.sources.url = FileManifestUrlSource(downloadUrl) + fileManifest.sources.url = FileManifestUrlSource(downloadUrl, FileManifestHashes( + sha1 = sha1, + sha512 = sha512, + murmur2 = murmur2, + )) } else { terminal.warning("File ${file.path} has multiple valid URL sources; only the first will be included") } diff --git a/src/main/kotlin/io/github/sculk_cli/commands/Install.kt b/src/main/kotlin/io/github/sculk_cli/commands/Install.kt index ace0482..50d7cd4 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/Install.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/Install.kt @@ -102,13 +102,6 @@ class Install : installedItems += fileFile.path.toString() - if (fileFile.exists()) { - if (fileFile.readBytes().digestSha512() == fileManifest.hashes.sha512) { - terminal.info("Skipping ${file.path} because it's already downloaded") - return@launch - } - } - val downloadLink = if (fileManifest.sources.url != null) { fileManifest.sources.url.url } else if (fileManifest.sources.modrinth != null) { @@ -119,6 +112,23 @@ class Install : error("No valid source found for ${file.path}") } + val hashes = if (fileManifest.sources.url != null) { + fileManifest.sources.url.hashes + } else if (fileManifest.sources.modrinth != null) { + fileManifest.sources.modrinth.hashes + } else if (fileManifest.sources.curseforge != null) { + fileManifest.sources.curseforge.hashes + } else { + error("No valid hashes found for ${file.path}") + } + + if (fileFile.exists()) { + if (fileFile.readBytes().digestSha512() == hashes.sha512) { + terminal.info("Skipping ${file.path} because it's already downloaded") + return@launch + } + } + fileFile.parentFile.mkdirs() val request = ctx.client.get(downloadLink) { timeout { @@ -128,7 +138,7 @@ class Install : } fileFile.writeBytes(request.readRawBytes()) - if (fileFile.readBytes().digestSha512() != fileManifest.hashes.sha512) { + if (fileFile.readBytes().digestSha512() != hashes.sha512) { error("Downloaded file for ${file.path} was corrupted or hash was incorrect") } diff --git a/src/main/kotlin/io/github/sculk_cli/commands/Link.kt b/src/main/kotlin/io/github/sculk_cli/commands/Link.kt index 3aa1686..aa7cecb 100644 --- a/src/main/kotlin/io/github/sculk_cli/commands/Link.kt +++ b/src/main/kotlin/io/github/sculk_cli/commands/Link.kt @@ -55,9 +55,19 @@ class Link : CliktCommand( ctx: Context, manifest: Map.Entry ) { + val hashes = if (manifest.value.sources.url != null) { + manifest.value.sources.url!!.hashes + } else if (manifest.value.sources.modrinth != null) { + manifest.value.sources.modrinth!!.hashes + } else if (manifest.value.sources.curseforge != null) { + manifest.value.sources.curseforge!!.hashes + } else { + error("No valid hashes found for ${manifest.key}") + } + when (target) { Target.Curseforge -> { - val matches = ctx.curseforge.getFingerprintMatches(manifest.value.hashes.murmur2) + val matches = ctx.curseforge.getFingerprintMatches(hashes.murmur2) if (matches.isEmpty()) { terminal.info("No match found for ${manifest.key}") @@ -85,7 +95,7 @@ class Link : CliktCommand( Target.Modrinth -> { val version = ctx.modrinth.getVersionFromHash( - manifest.value.hashes.sha512, + hashes.sha512, ModrinthHashAlgorithm.SHA512 ) diff --git a/src/main/kotlin/io/github/sculk_cli/pack/InMemory.kt b/src/main/kotlin/io/github/sculk_cli/pack/InMemory.kt index 82abe82..a13b20e 100644 --- a/src/main/kotlin/io/github/sculk_cli/pack/InMemory.kt +++ b/src/main/kotlin/io/github/sculk_cli/pack/InMemory.kt @@ -173,7 +173,6 @@ data class PackManifestFile( data class FileManifest( var filename: String, var side: Side, - var hashes: FileManifestHashes, var fileSize: Int, var sources: FileManifestSources ) @@ -189,15 +188,15 @@ data class FileManifestSources( ) data class FileManifestCurseforgeSource( - var projectId: Int, var fileUrl: String, var fileId: Int, + var projectId: Int, var fileUrl: String, var fileId: Int, var hashes: FileManifestHashes, ) data class FileManifestModrinthSource( - var projectId: String, var fileUrl: String, + var projectId: String, var fileUrl: String, var hashes: FileManifestHashes, ) data class FileManifestUrlSource( - var url: String + var url: String, var hashes: FileManifestHashes, ) fun PackManifest.toSerial(): SerialPackManifest { @@ -232,11 +231,6 @@ fun FileManifest.toSerial(): SerialFileManifest { return SerialFileManifest( filename = filename, side = side, - hashes = SerialFileManifestHashes( - sha1 = hashes.sha1, - sha512 = hashes.sha512, - murmur2 = hashes.murmur2, - ), fileSize = fileSize, sources = SerialFileManifestSources( curseforge = sources.curseforge?.let { @@ -244,19 +238,30 @@ fun FileManifest.toSerial(): SerialFileManifest { projectId = it.projectId, fileUrl = it.fileUrl, fileId = it.fileId, + hashes = it.hashes.toSerial(), ) }, modrinth = sources.modrinth?.let { SerialFileManifestModrinthSource( projectId = it.projectId, fileUrl = it.fileUrl, + hashes = it.hashes.toSerial(), ) }, url = sources.url?.let { SerialFileManifestUrlSource( url = it.url, + hashes = it.hashes.toSerial(), ) }, ) ) } + +fun FileManifestHashes.toSerial(): SerialFileManifestHashes { + return SerialFileManifestHashes( + sha1 = sha1, + sha512 = sha512, + murmur2 = murmur2, + ) +} diff --git a/src/main/kotlin/io/github/sculk_cli/pack/Serial.kt b/src/main/kotlin/io/github/sculk_cli/pack/Serial.kt index 47635de..964888e 100644 --- a/src/main/kotlin/io/github/sculk_cli/pack/Serial.kt +++ b/src/main/kotlin/io/github/sculk_cli/pack/Serial.kt @@ -36,7 +36,6 @@ data class SerialPackManifestFile( data class SerialFileManifest( val filename: String, val side: Side, - val hashes: SerialFileManifestHashes, val fileSize: Int, val sources: SerialFileManifestSources ) @@ -55,17 +54,17 @@ data class SerialFileManifestSources( @Serializable data class SerialFileManifestCurseforgeSource( - val projectId: Int, val fileUrl: String, val fileId: Int, + val projectId: Int, val fileUrl: String, val fileId: Int, val hashes: SerialFileManifestHashes, ) @Serializable data class SerialFileManifestModrinthSource( - val projectId: String, val fileUrl: String, + val projectId: String, val fileUrl: String, val hashes: SerialFileManifestHashes, ) @Serializable data class SerialFileManifestUrlSource( - var url: String + var url: String, val hashes: SerialFileManifestHashes, ) fun SerialPackManifest.load(): PackManifest { @@ -98,29 +97,36 @@ fun SerialPackManifest.load(): PackManifest { fun SerialFileManifest.load(): FileManifest { return FileManifest( - filename = filename, side = side, hashes = FileManifestHashes( - sha1 = hashes.sha1, - sha512 = hashes.sha512, - murmur2 = hashes.murmur2, - ), fileSize = fileSize, sources = FileManifestSources( + filename = filename, side = side, fileSize = fileSize, sources = FileManifestSources( curseforge = sources.curseforge?.let { FileManifestCurseforgeSource( projectId = it.projectId, fileUrl = it.fileUrl, fileId = it.fileId, + hashes = it.hashes.load(), ) }, modrinth = sources.modrinth?.let { FileManifestModrinthSource( projectId = it.projectId, fileUrl = it.fileUrl, + hashes = it.hashes.load(), ) }, url = sources.url?.let { FileManifestUrlSource( url = it.url, + hashes = it.hashes.load(), ) }, ) ) } + +fun SerialFileManifestHashes.load(): FileManifestHashes { + return FileManifestHashes( + sha1 = sha1, + sha512 = sha512, + murmur2 = murmur2, + ) +} diff --git a/src/main/kotlin/io/github/sculk_cli/util/CurseforgeUtil.kt b/src/main/kotlin/io/github/sculk_cli/util/CurseforgeUtil.kt index 2110c8e..025c2ff 100644 --- a/src/main/kotlin/io/github/sculk_cli/util/CurseforgeUtil.kt +++ b/src/main/kotlin/io/github/sculk_cli/util/CurseforgeUtil.kt @@ -120,25 +120,19 @@ suspend fun addCurseforgeFile( error("Existing manifest already has a Curseforge source (did you mean to use the update command?)") } - if (existingManifest.hashes.sha1 != sha1 || existingManifest.hashes.sha512 != sha512) { - error("File hashes do not match for ${file.fileName}") - } - existingManifest.sources.curseforge = FileManifestCurseforgeSource( - projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id + projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id, hashes = FileManifestHashes(sha1, sha512, murmur2) ) existingManifest } else { FileManifest( - filename = file.fileName, hashes = FileManifestHashes( - sha1 = sha1, sha512 = sha512, murmur2 = murmur2 - ), + filename = file.fileName, fileSize = tempFile.size, side = file.getSide().toSide(), sources = FileManifestSources( curseforge = FileManifestCurseforgeSource( - projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id + projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id, hashes = FileManifestHashes(sha1, sha512, murmur2) ), modrinth = null, url = null ) ) @@ -242,13 +236,11 @@ suspend fun updateCurseforgeProject( } val tempFile = downloadFileTemp(parseUrl(file.downloadUrl!!)).readBytes() - manifest.hashes.sha1 = tempFile.digestSha1() - manifest.hashes.sha512 = tempFile.digestSha512() manifest.fileSize = tempFile.size manifest.filename = file.fileName manifest.sources.curseforge = FileManifestCurseforgeSource( - projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id + projectId = mod.id, fileUrl = file.downloadUrl!!, fileId = file.id, hashes = FileManifestHashes(tempFile.digestSha1(), tempFile.digestSha512(), tempFile.digestMurmur2()) ) return true diff --git a/src/main/kotlin/io/github/sculk_cli/util/ModrinthUtil.kt b/src/main/kotlin/io/github/sculk_cli/util/ModrinthUtil.kt index b981a46..a69392e 100644 --- a/src/main/kotlin/io/github/sculk_cli/util/ModrinthUtil.kt +++ b/src/main/kotlin/io/github/sculk_cli/util/ModrinthUtil.kt @@ -83,6 +83,7 @@ suspend fun addModrinthVersion( ): Boolean { val modrinthFile = version.files.first { it.primary } val tempFile = downloadFileTemp(parseUrl(modrinthFile.downloadUrl)) + val bytes = tempFile.readBytes() val dir = version.loaders.first().getSaveDir() val path = manifestPath ?: "$dir/${project.slug}.sculk.json" @@ -96,26 +97,22 @@ suspend fun addModrinthVersion( error("Existing manifest already has a Modrinth source (did you mean to use the update command?)") } - if (existingManifest.hashes.sha1 != modrinthFile.hashes.sha1 || existingManifest.hashes.sha512 != modrinthFile.hashes.sha512) { - error("File hashes do not match for ${modrinthFile.filename}") - } - existingManifest.sources.modrinth = FileManifestModrinthSource( - projectId = project.id, fileUrl = modrinthFile.downloadUrl + projectId = project.id, fileUrl = modrinthFile.downloadUrl, hashes = FileManifestHashes(modrinthFile.hashes.sha1, modrinthFile.hashes.sha512, bytes.digestMurmur2()) ) existingManifest } else { FileManifest( - filename = modrinthFile.filename, hashes = FileManifestHashes( - sha1 = modrinthFile.hashes.sha1, - sha512 = modrinthFile.hashes.sha512, - murmur2 = tempFile.readBytes().digestMurmur2() - ), fileSize = tempFile.readBytes().size, side = modrinthEnvTypePairToSide( + filename = modrinthFile.filename, fileSize = tempFile.readBytes().size, side = modrinthEnvTypePairToSide( project.clientSideSupport, project.serverSideSupport ), sources = FileManifestSources( curseforge = null, modrinth = FileManifestModrinthSource( - projectId = project.id, fileUrl = modrinthFile.downloadUrl + projectId = project.id, fileUrl = modrinthFile.downloadUrl, hashes = FileManifestHashes( + sha1 = modrinthFile.hashes.sha1, + sha512 = modrinthFile.hashes.sha512, + murmur2 = bytes.digestMurmur2() + ), ), url = null ) ) @@ -203,13 +200,11 @@ suspend fun updateModrinthProject( } val tempFile = downloadFileTemp(parseUrl(file.downloadUrl)).readBytes() - manifest.hashes.sha1 = tempFile.digestSha1() - manifest.hashes.sha512 = tempFile.digestSha512() manifest.fileSize = tempFile.size manifest.filename = file.filename manifest.sources.modrinth = FileManifestModrinthSource( - projectId = mod.id, fileUrl = file.downloadUrl + projectId = mod.id, fileUrl = file.downloadUrl, hashes = FileManifestHashes(tempFile.digestSha1(), tempFile.digestSha512(), tempFile.digestMurmur2()) ) return true