diff --git a/IconExporter/README.md b/IconExporter/README.md index 14869b3..69465db 100644 --- a/IconExporter/README.md +++ b/IconExporter/README.md @@ -48,19 +48,27 @@ ## ๐Ÿ“ ์ถœ๋ ฅ ๊ตฌ์กฐ -์ถ”์ถœ๋œ ์•„์ด์ฝ˜์€ `.minecraft/icons/` ํด๋”์— ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. +์•„์ด์ฝ˜์€ `.minecraft/icons/` ํด๋”์— **blocks/items**๋กœ ๊ตฌ๋ถ„๋˜์–ด ์ €์žฅ๋ฉ๋‹ˆ๋‹ค. +์™„๋ฃŒ ํ›„ ์ž๋™์œผ๋กœ ZIP ํŒŒ์ผ์ด ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค. ``` .minecraft/ -โ””โ”€โ”€ icons/ - โ”œโ”€โ”€ minecraft/ - โ”‚ โ”œโ”€โ”€ diamond.png - โ”‚ โ”œโ”€โ”€ iron_ingot.png - โ”‚ โ””โ”€โ”€ ... - โ””โ”€โ”€ create/ - โ”œโ”€โ”€ mechanical_press.png - โ”œโ”€โ”€ brass_ingot.png - โ””โ”€โ”€ ... +โ”œโ”€โ”€ icons/ +โ”‚ โ”œโ”€โ”€ minecraft/ +โ”‚ โ”‚ โ”œโ”€โ”€ blocks/ +โ”‚ โ”‚ โ”‚ โ”œโ”€โ”€ stone.png +โ”‚ โ”‚ โ”‚ โ””โ”€โ”€ ... +โ”‚ โ”‚ โ””โ”€โ”€ items/ +โ”‚ โ”‚ โ”œโ”€โ”€ diamond.png +โ”‚ โ”‚ โ””โ”€โ”€ ... +โ”‚ โ””โ”€โ”€ create/ +โ”‚ โ”œโ”€โ”€ blocks/ +โ”‚ โ”‚ โ”œโ”€โ”€ brass_casing.png +โ”‚ โ”‚ โ””โ”€โ”€ ... +โ”‚ โ””โ”€โ”€ items/ +โ”‚ โ”œโ”€โ”€ brass_ingot.png +โ”‚ โ””โ”€โ”€ ... +โ””โ”€โ”€ icons_create_20251226_181500.zip โ† ์ž๋™ ์ƒ์„ฑ ``` --- diff --git a/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt b/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt index 1bc8057..ac1c9c1 100644 --- a/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt +++ b/IconExporter/src/main/kotlin/com/beemer/iconexporter/export/IconExportManager.kt @@ -2,20 +2,27 @@ package com.beemer.iconexporter.export import com.beemer.iconexporter.IconExporter import com.beemer.iconexporter.render.IconRenderer +import java.io.FileOutputStream import java.io.IOException import java.nio.file.Files +import java.nio.file.Path import java.util.* +import java.util.zip.ZipEntry +import java.util.zip.ZipOutputStream import kotlin.io.path.exists +import kotlin.io.path.name +import kotlin.io.path.pathString import net.minecraft.client.Minecraft import net.minecraft.core.registries.BuiltInRegistries import net.minecraft.network.chat.Component +import net.minecraft.world.item.BlockItem import net.minecraft.world.item.Item import net.minecraft.world.item.ItemStack /** * ์•„์ด์ฝ˜ ์ถ”์ถœ ๊ด€๋ฆฌ์ž * - * ๋Œ€๋Ÿ‰์˜ ์•„์ดํ…œ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ฒŒ์ž„ ํ”„๋ฆฌ์ฆˆ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ‹ฑ ๊ธฐ๋ฐ˜ ํ ์‹œ์Šคํ…œ์œผ๋กœ ์•„์ดํ…œ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. + * ๋Œ€๋Ÿ‰์˜ ์•„์ดํ…œ์„ ์ฒ˜๋ฆฌํ•  ๋•Œ ๊ฒŒ์ž„ ํ”„๋ฆฌ์ฆˆ๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ํ‹ฑ ๊ธฐ๋ฐ˜ ํ ์‹œ์Šคํ…œ์œผ๋กœ ์•„์ดํ…œ์„ ์ˆœ์ฐจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์™„๋ฃŒ ํ›„ icons ํด๋”๋ฅผ ZIP์œผ๋กœ ์••์ถ•ํ•ฉ๋‹ˆ๋‹ค. */ object IconExportManager { // ํ‹ฑ๋‹น ์ฒ˜๋ฆฌํ•  ์•„์ดํ…œ ์ˆ˜ (๋„ˆ๋ฌด ๋†’์œผ๋ฉด ๋ž™, ๋„ˆ๋ฌด ๋‚ฎ์œผ๋ฉด ๋А๋ฆผ) @@ -127,8 +134,12 @@ object IconExportManager { } try { + // ์•„์ดํ…œ์ด ๋ธ”๋ก์ธ์ง€ ํ™•์ธ (BlockItem์ด๋ฉด ๋ธ”๋ก, ์•„๋‹ˆ๋ฉด ์•„์ดํ…œ) + val isBlock = item is BlockItem + val type = if (isBlock) "blocks" else "items" + // ์ €์žฅ ๊ฒฝ๋กœ ์ƒ์„ฑ - val outputPath = getOutputPath(itemId.namespace, itemId.path) + val outputPath = getOutputPath(itemId.namespace, type, itemId.path) // ์ด๋ฏธ ํŒŒ์ผ์ด ์กด์žฌํ•˜๊ณ  ๋ฎ์–ด์“ฐ๊ธฐ๊ฐ€ ๋น„ํ™œ์„ฑํ™”๋œ ๊ฒฝ์šฐ ์Šคํ‚ต if (outputPath.exists() && !overwrite) { @@ -162,13 +173,16 @@ object IconExportManager { } } - /** ์•„์ดํ…œ ID๋กœ ์ถœ๋ ฅ ๊ฒฝ๋กœ ์ƒ์„ฑ */ - private fun getOutputPath(namespace: String, path: String): java.nio.file.Path { + /** + * ์•„์ดํ…œ ID๋กœ ์ถœ๋ ฅ ๊ฒฝ๋กœ ์ƒ์„ฑ ํ˜•์‹: icons///.png ์˜ˆ: + * icons/create/blocks/brass_casing.png + */ + private fun getOutputPath(namespace: String, type: String, path: String): Path { val mc = Minecraft.getInstance() val gameDir = mc.gameDirectory.toPath() - // icons//.png - return gameDir.resolve("icons").resolve(namespace).resolve("$path.png") + // icons///.png + return gameDir.resolve("icons").resolve(namespace).resolve(type).resolve("$path.png") } /** ์ง„ํ–‰๋ฅ  ๋กœ๊ทธ ์ถœ๋ ฅ */ @@ -199,6 +213,17 @@ object IconExportManager { isRunning = false val elapsed = System.currentTimeMillis() - startTime + val mc = Minecraft.getInstance() + val iconsDir = mc.gameDirectory.toPath().resolve("icons") + + // ZIP ์••์ถ• ์ƒ์„ฑ + var zipPath: Path? = null + try { + zipPath = createZipArchive(iconsDir) + } catch (e: Exception) { + IconExporter.LOGGER.error("ZIP ์••์ถ• ์‹คํŒจ: {}", e.message) + } + val summary = """ ยงa=== ์ถ”์ถœ ์™„๋ฃŒ === @@ -221,19 +246,65 @@ object IconExportManager { } // ๊ฒŒ์ž„ ๋‚ด ๋ฉ”์‹œ์ง€ - val mc = Minecraft.getInstance() mc.player?.let { player -> player.displayClientMessage(Component.literal(summary), false) + // ZIP ๊ฒฝ๋กœ ์•ˆ๋‚ด + if (zipPath != null) { + player.displayClientMessage( + Component.literal("ยง7ZIP ํŒŒ์ผ: ยงf${zipPath.toAbsolutePath()}"), + false + ) + } + // ์ €์žฅ ์œ„์น˜ ์•ˆ๋‚ด - val outputDir = mc.gameDirectory.toPath().resolve("icons") player.displayClientMessage( - Component.literal("ยง7์ €์žฅ ์œ„์น˜: ยงf${outputDir.toAbsolutePath()}"), + Component.literal("ยง7์•„์ด์ฝ˜ ํด๋”: ยงf${iconsDir.toAbsolutePath()}"), false ) } } + /** icons ํด๋”๋ฅผ ZIP์œผ๋กœ ์••์ถ• ํŒŒ์ผ๋ช…: icons__.zip */ + private fun createZipArchive(iconsDir: Path): Path? { + if (!iconsDir.exists()) { + return null + } + + val mc = Minecraft.getInstance() + val gameDir = mc.gameDirectory.toPath() + + // ZIP ํŒŒ์ผ๋ช… ์ƒ์„ฑ + val timestamp = + java.time.LocalDateTime.now() + .format(java.time.format.DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss")) + val modName = currentModFilter ?: "all" + val zipFileName = "icons_${modName}_$timestamp.zip" + val zipPath = gameDir.resolve(zipFileName) + + IconExporter.LOGGER.info("ZIP ์••์ถ• ์ƒ์„ฑ ์ค‘: {}", zipPath) + + ZipOutputStream(FileOutputStream(zipPath.toFile())).use { zos -> + Files.walk(iconsDir).use { paths -> + paths.filter { Files.isRegularFile(it) && it.name.endsWith(".png") }.forEach { file + -> + try { + // ZIP ๋‚ด ์ƒ๋Œ€ ๊ฒฝ๋กœ: //.png + val relativePath = iconsDir.relativize(file).pathString.replace("\\", "/") + zos.putNextEntry(ZipEntry(relativePath)) + Files.copy(file, zos) + zos.closeEntry() + } catch (e: Exception) { + IconExporter.LOGGER.error("ZIP์— ํŒŒ์ผ ์ถ”๊ฐ€ ์‹คํŒจ: {} - {}", file, e.message) + } + } + } + } + + IconExporter.LOGGER.info("ZIP ์••์ถ• ์™„๋ฃŒ: {}", zipPath) + return zipPath + } + /** ์ž‘์—… ์ทจ์†Œ */ fun cancel() { if (!isRunning) return