unity_pack/
meta.rs

1//! Unity .meta file generation
2//!
3//! Each asset in Unity has an accompanying .meta file that contains:
4//! - The asset's GUID
5//! - Import settings specific to the asset type
6
7use crate::UnityGuid;
8
9/// Type of Unity importer to use for an asset
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ImporterType {
12    /// Default importer for unknown file types
13    Default,
14    /// Text/script files (.cs, .js, .txt, .json, .xml)
15    Text,
16    /// Texture files (.png, .jpg, .tga, .psd)
17    Texture,
18    /// Raw heightmap data
19    RawHeightmap,
20    /// Native Unity asset (.asset, .prefab, .mat)
21    NativeFormat,
22    /// Folder
23    Folder,
24}
25
26impl ImporterType {
27    /// Detect importer type from file extension
28    pub fn from_extension(ext: &str) -> Self {
29        match ext.to_lowercase().as_str() {
30            // Scripts and text
31            "cs" | "js" | "txt" | "json" | "xml" | "yaml" | "yml" | "md" => Self::Text,
32            // Textures
33            "png" | "jpg" | "jpeg" | "tga" | "psd" | "bmp" | "gif" | "tif" | "tiff" => {
34                Self::Texture
35            }
36            // Raw data (often heightmaps)
37            "raw" | "r16" | "r32" => Self::RawHeightmap,
38            // Native Unity formats
39            "asset" | "prefab" | "mat" | "unity" | "controller" | "anim" => Self::NativeFormat,
40            // Default for unknown
41            _ => Self::Default,
42        }
43    }
44}
45
46/// Represents a Unity .meta file
47#[derive(Debug, Clone)]
48pub struct MetaFile {
49    pub guid: UnityGuid,
50    pub importer_type: ImporterType,
51    pub is_folder: bool,
52}
53
54impl MetaFile {
55    /// Create a new meta file for an asset
56    pub fn new(guid: UnityGuid, importer_type: ImporterType) -> Self {
57        Self {
58            guid,
59            importer_type,
60            is_folder: false,
61        }
62    }
63
64    /// Create a meta file for a folder
65    pub fn folder(guid: UnityGuid) -> Self {
66        Self {
67            guid,
68            importer_type: ImporterType::Folder,
69            is_folder: true,
70        }
71    }
72
73    /// Generate the .meta file content as a string
74    pub fn to_yaml(&self) -> String {
75        let guid_str = self.guid.to_hex();
76
77        match self.importer_type {
78            ImporterType::Folder => format!(
79                r#"fileFormatVersion: 2
80guid: {}
81folderAsset: yes
82DefaultImporter:
83  externalObjects: {{}}
84  userData: 
85  assetBundleName: 
86  assetBundleVariant: 
87"#,
88                guid_str
89            ),
90
91            ImporterType::Text => format!(
92                r#"fileFormatVersion: 2
93guid: {}
94TextScriptImporter:
95  externalObjects: {{}}
96  userData: 
97  assetBundleName: 
98  assetBundleVariant: 
99"#,
100                guid_str
101            ),
102
103            ImporterType::Texture => format!(
104                r#"fileFormatVersion: 2
105guid: {}
106TextureImporter:
107  internalIDToNameTable: []
108  externalObjects: {{}}
109  serializedVersion: 12
110  mipmaps:
111    mipMapMode: 0
112    enableMipMap: 1
113    sRGBTexture: 1
114    linearTexture: 0
115    fadeOut: 0
116    borderMipMap: 0
117    mipMapsPreserveCoverage: 0
118    alphaTestReferenceValue: 0.5
119    mipMapFadeDistanceStart: 1
120    mipMapFadeDistanceEnd: 3
121  bumpmap:
122    convertToNormalMap: 0
123    externalNormalMap: 0
124    heightScale: 0.25
125    normalMapFilter: 0
126  isReadable: 0
127  streamingMipmaps: 0
128  streamingMipmapsPriority: 0
129  grayScaleToAlpha: 0
130  generateCubemap: 6
131  cubemapConvolution: 0
132  seamlessCubemap: 0
133  textureFormat: 1
134  maxTextureSize: 2048
135  textureSettings:
136    serializedVersion: 2
137    filterMode: 1
138    aniso: 1
139    mipBias: 0
140    wrapU: 0
141    wrapV: 0
142    wrapW: 0
143  nPOTScale: 1
144  lightmap: 0
145  compressionQuality: 50
146  spriteMode: 0
147  spriteExtrude: 1
148  spriteMeshType: 1
149  alignment: 0
150  spritePivot: {{x: 0.5, y: 0.5}}
151  spritePixelsToUnits: 100
152  spriteBorder: {{x: 0, y: 0, z: 0, w: 0}}
153  spriteGenerateFallbackPhysicsShape: 1
154  alphaUsage: 1
155  alphaIsTransparency: 0
156  spriteTessellationDetail: -1
157  textureType: 0
158  textureShape: 1
159  singleChannelComponent: 0
160  userData: 
161  assetBundleName: 
162  assetBundleVariant: 
163"#,
164                guid_str
165            ),
166
167            ImporterType::RawHeightmap => format!(
168                r#"fileFormatVersion: 2
169guid: {}
170DefaultImporter:
171  externalObjects: {{}}
172  userData: 
173  assetBundleName: 
174  assetBundleVariant: 
175"#,
176                guid_str
177            ),
178
179            ImporterType::NativeFormat => format!(
180                r#"fileFormatVersion: 2
181guid: {}
182NativeFormatImporter:
183  externalObjects: {{}}
184  mainObjectFileID: 11400000
185  userData: 
186  assetBundleName: 
187  assetBundleVariant: 
188"#,
189                guid_str
190            ),
191
192            ImporterType::Default => format!(
193                r#"fileFormatVersion: 2
194guid: {}
195DefaultImporter:
196  externalObjects: {{}}
197  userData: 
198  assetBundleName: 
199  assetBundleVariant: 
200"#,
201                guid_str
202            ),
203        }
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_importer_detection() {
213        assert_eq!(ImporterType::from_extension("cs"), ImporterType::Text);
214        assert_eq!(ImporterType::from_extension("png"), ImporterType::Texture);
215        assert_eq!(
216            ImporterType::from_extension("raw"),
217            ImporterType::RawHeightmap
218        );
219        assert_eq!(
220            ImporterType::from_extension("asset"),
221            ImporterType::NativeFormat
222        );
223        assert_eq!(ImporterType::from_extension("xyz"), ImporterType::Default);
224    }
225
226    #[test]
227    fn test_meta_yaml_generation() {
228        let guid = UnityGuid::from_hex("0123456789abcdef0123456789abcdef").unwrap();
229        let meta = MetaFile::new(guid, ImporterType::Text);
230        let yaml = meta.to_yaml();
231
232        assert!(yaml.contains("fileFormatVersion: 2"));
233        assert!(yaml.contains("guid: 0123456789abcdef0123456789abcdef"));
234        assert!(yaml.contains("TextScriptImporter:"));
235    }
236}