@@ -7,9 +7,10 @@ import DirSelect from '@/components/DirSelect'
77import { openImageFileDialog , createTrophy , createNamePlate , createAvatarAccessory , createMapIcon , getResourceList , getLocalImagePreviewUrl } from '@/api/customResource'
88import { openAfbFileDialog , openAfbFolderDialog , extractDds } from '@/api/ddsExtractor'
99import type { ExtractResult } from '@/api/ddsExtractor'
10+ import { convertImgToDds } from '@/api/imgToDds'
1011import CharaCreator from './CharaCreator'
1112
12- type ModalType = null | 'trophy' | 'namePlate' | 'avatarAccessory' | 'mapIcon' | 'ddsExtractor'
13+ type ModalType = null | 'trophy' | 'namePlate' | 'avatarAccessory' | 'mapIcon' | 'ddsExtractor' | 'imgToDds'
1314
1415export default defineComponent ( {
1516 setup ( ) {
@@ -22,6 +23,14 @@ export default defineComponent({
2223 const ddsExtracting = ref ( false )
2324 const ddsResults = ref < ExtractResult [ ] > ( [ ] )
2425
26+ // imgtodds
27+ const imgPath = ref ( '' )
28+ const converting = ref ( false )
29+ const imgFormat = ref < 'bc1' | 'bc3' | 'bc7' > ( 'bc1' )
30+ const imgWidth = ref ( 0 )
31+ const imgHeight = ref ( 0 )
32+ const generateMipMaps = ref ( false )
33+
2534 const targetDir = ref ( '' )
2635 const resourceId = ref ( 9000 )
2736 const resourceName = ref ( '' )
@@ -67,8 +76,16 @@ export default defineComponent({
6776 { icon : 'i-mdi-hanger' , labelKey : 'tools.createAvatarAccessory' , action : ( ) => openModal ( 'avatarAccessory' ) , experimental : true } ,
6877 { icon : 'i-mdi-map-marker' , labelKey : 'tools.createMapIcon' , action : ( ) => openModal ( 'mapIcon' ) , experimental : true } ,
6978 { icon : 'i-mdi-account' , labelKey : 'tools.createChara' , action : ( ) => { showCharaCreator . value = true } , experimental : true } ,
79+ { icon : 'i-mdi-image-multiple' , labelKey : 'imgToDds.title' , action : ( ) => openModal ( 'imgToDds' ) , experimental : false } ,
80+ ] )
81+
82+ const formatOptions = computed < SelectOption [ ] > ( ( ) => [
83+ { label : t ( 'imgToDds.bc1' ) , value : 'bc1' } ,
84+ { label : t ( 'imgToDds.bc3' ) , value : 'bc3' } ,
85+ { label : t ( 'imgToDds.bc7' ) , value : 'bc7' } ,
7086 ] )
7187
88+
7289 function openModal ( type : ModalType ) {
7390 activeModal . value = type
7491 resourceId . value = 9000
@@ -80,6 +97,7 @@ export default defineComponent({
8097 textureImagePath . value = ''
8198 accessoryCategory . value = 1
8299 idConflict . value = false
100+ imgPath . value = ''
83101 const custom = optionDirs . value . filter ( d => d . dirName !== 'A000' )
84102 if ( custom . length > 0 && ! targetDir . value )
85103 targetDir . value = custom [ 0 ] . dirName
@@ -94,6 +112,11 @@ export default defineComponent({
94112 if ( path ) imagePath . value = path
95113 }
96114
115+ async function selectImgToDdsImage ( ) {
116+ const path = await openImageFileDialog ( )
117+ if ( path ) imgPath . value = path
118+ }
119+
97120 async function selectIconImage ( ) {
98121 const path = await openImageFileDialog ( )
99122 if ( path ) iconImagePath . value = path
@@ -134,6 +157,30 @@ export default defineComponent({
134157 }
135158 }
136159
160+ async function handleConvert ( ) {
161+ if ( ! imgPath . value ) {
162+ addToast ( { message : t ( 'imgToDds.noImage' ) , type : 'error' } )
163+ return
164+ }
165+ converting . value = true
166+ try {
167+ await convertImgToDds ( {
168+ sourcePath : imgPath . value ,
169+ format : imgFormat . value ,
170+ width : imgWidth . value || undefined ,
171+ height : imgHeight . value || undefined ,
172+ generateMipMaps : generateMipMaps . value ,
173+ } )
174+ addToast ( { message : t ( 'imgToDds.success' ) , type : 'success' } )
175+ closeModal ( )
176+ } catch ( e : any ) {
177+ const msg = e ?. response ?. data || e ?. message || t ( 'imgToDds.failed' )
178+ addToast ( { message : String ( msg ) , type : 'error' } )
179+ } finally {
180+ converting . value = false
181+ }
182+ }
183+
137184 async function checkIdConflict ( ) {
138185 if ( ! activeModal . value ) return
139186 idChecking . value = true
@@ -222,6 +269,7 @@ export default defineComponent({
222269 case 'namePlate' : return t ( 'tools.createNamePlate' )
223270 case 'avatarAccessory' : return t ( 'tools.createAvatarAccessory' )
224271 case 'mapIcon' : return t ( 'tools.createMapIcon' )
272+ case 'imgToDds' : return t ( 'imgToDds.title' )
225273 default : return ''
226274 }
227275 } )
@@ -317,6 +365,33 @@ export default defineComponent({
317365 < Button onClick = { handleExtractDds } ing = { ddsExtracting . value } > { t ( 'ddsExtractor.extract' ) } </ Button >
318366 </ div >
319367 </ div >
368+ ) : activeModal . value === 'imgToDds' ? (
369+ < div class = "flex flex-col gap-3 p-2" >
370+ < p class = "text-sm op-50" > { t ( 'imgToDds.desc' ) } </ p >
371+ { renderImageSelector ( imgPath , selectImgToDdsImage , t ( 'imgToDds.selectImage' ) ) }
372+ < div >
373+ < label class = "block text-sm op-60 mb-1" > { t ( 'imgToDds.format' ) } </ label >
374+ < Select options = { formatOptions . value } v-model :value = { imgFormat . value } />
375+ </ div >
376+ < div class = "flex gap-3" >
377+ < div class = "flex-1" >
378+ < label class = "block text-sm op-60 mb-1" > { t ( 'imgToDds.width' ) } </ label >
379+ < NumberInput v-model :value = { imgWidth . value } min = { 0 } max = { 8192 } placeholder = { t ( 'imgToDds.auto' ) } />
380+ </ div >
381+ < div class = "flex-1" >
382+ < label class = "block text-sm op-60 mb-1" > { t ( 'imgToDds.height' ) } </ label >
383+ < NumberInput v-model :value = { imgHeight . value } min = { 0 } max = { 8192 } placeholder = { t ( 'imgToDds.auto' ) } />
384+ </ div >
385+ </ div >
386+ < label class = "flex items-center gap-2 text-sm cursor-pointer" >
387+ < input type = "checkbox" v-model = { generateMipMaps . value } class = "checkbox checkbox-primary" />
388+ < span > { t ( 'imgToDds.generateMipMaps' ) } </ span >
389+ </ label >
390+ < div class = "flex justify-end gap-2 mt-2" >
391+ < Button onClick = { closeModal } > { t ( 'common.cancel' ) } </ Button >
392+ < Button onClick = { handleConvert } ing = { converting . value } > { t ( 'imgToDds.convert' ) } </ Button >
393+ </ div >
394+ </ div >
320395 ) : (
321396 < div class = "flex flex-col gap-3 p-2" >
322397 < div >
0 commit comments