11// Analyze the exact structure of HeaderFooter record from sample.hwp
2+ use hwpers:: parser:: record:: { HwpTag , Record } ;
23use hwpers:: reader:: CfbReader ;
3- use hwpers:: parser:: record:: { Record , HwpTag } ;
44use hwpers:: reader:: StreamReader ;
55use hwpers:: utils:: compression:: decompress_stream;
66
@@ -9,31 +9,31 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
99
1010 let file_path = "/Users/indo/Downloads/sample.hwp" ;
1111 let mut reader = CfbReader :: from_file ( file_path) ?;
12-
12+
1313 // Parse FileHeader
1414 let header_data = reader. read_stream ( "FileHeader" ) ?;
1515 let header = hwpers:: parser:: header:: FileHeader :: parse ( header_data) ?;
16-
16+
1717 // Analyze BodyText/Section0
1818 let section_data = reader. read_stream ( "BodyText/Section0" ) ?;
19-
19+
2020 // Decompress if needed
2121 let data = if header. is_compressed ( ) {
2222 decompress_stream ( & section_data) ?
2323 } else {
2424 section_data
2525 } ;
26-
26+
2727 // Find HeaderFooter record
2828 let mut stream_reader = StreamReader :: new ( data) ;
29-
29+
3030 while stream_reader. remaining ( ) >= 8 {
3131 let position = stream_reader. position ( ) ;
32-
32+
3333 match Record :: parse ( & mut stream_reader) {
3434 Ok ( record) => {
3535 let tag = HwpTag :: from_u16 ( record. tag_id ( ) ) ;
36-
36+
3737 if tag == Some ( HwpTag :: HeaderFooter ) {
3838 analyze_header_footer_detailed ( & record, position) ;
3939 break ; // Only analyze the first one
@@ -42,7 +42,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
4242 Err ( _) => break ,
4343 }
4444 }
45-
45+
4646 Ok ( ( ) )
4747}
4848
@@ -51,12 +51,12 @@ fn analyze_header_footer_detailed(record: &Record, position: u64) {
5151 println ! ( " 위치: 0x{:08X}" , position) ;
5252 println ! ( " 크기: {} bytes" , record. data. len( ) ) ;
5353 println ! ( ) ;
54-
54+
5555 // Print complete hex dump
5656 println ! ( "📋 완전한 헥스 덤프:" ) ;
5757 print_hex_dump ( & record. data , 16 ) ;
5858 println ! ( ) ;
59-
59+
6060 // Try to parse as our current implementation expects
6161 println ! ( "🧪 현재 구현으로 파싱 시도:" ) ;
6262 match hwpers:: model:: header_footer:: HeaderFooter :: from_record ( record) {
@@ -74,31 +74,31 @@ fn analyze_header_footer_detailed(record: &Record, position: u64) {
7474 }
7575 }
7676 println ! ( ) ;
77-
77+
7878 // Manual analysis of the structure
7979 println ! ( "🔬 수동 구조 분석:" ) ;
80-
80+
8181 if record. data . len ( ) >= 40 {
8282 let mut reader = StreamReader :: new ( record. data . clone ( ) ) ;
83-
83+
8484 println ! ( " 바이트 단위 분석:" ) ;
8585 for i in 0 ..10 {
8686 if reader. remaining ( ) >= 4 {
8787 let value = reader. read_u32 ( ) . unwrap ( ) ;
8888 println ! ( " 오프셋 {}: 0x{:08X} ({})" , i * 4 , value, value) ;
8989 }
9090 }
91-
91+
9292 // Reset reader and try different interpretations
9393 reader = StreamReader :: new ( record. data . clone ( ) ) ;
94-
94+
9595 println ! ( ) ;
9696 println ! ( " 가능한 구조 해석:" ) ;
97-
97+
9898 // Interpretation 1: What we observed from the hex dump
9999 if record. data . len ( ) >= 40 {
100100 let field1 = reader. read_u32 ( ) . unwrap ( ) ; // 0x0000E888
101- let field2 = reader. read_u32 ( ) . unwrap ( ) ; // 0x000148DA
101+ let field2 = reader. read_u32 ( ) . unwrap ( ) ; // 0x000148DA
102102 let field3 = reader. read_u32 ( ) . unwrap ( ) ; // 0x00002138
103103 let field4 = reader. read_u32 ( ) . unwrap ( ) ; // 0x00002138
104104 let field5 = reader. read_u32 ( ) . unwrap ( ) ; // 0x00001624
@@ -107,18 +107,30 @@ fn analyze_header_footer_detailed(record: &Record, position: u64) {
107107 let field8 = reader. read_u32 ( ) . unwrap ( ) ; // 0x0000109C
108108 let field9 = reader. read_u32 ( ) . unwrap ( ) ; // 0x00000000
109109 let field10 = reader. read_u32 ( ) . unwrap ( ) ; // 0x00000000
110-
110+
111111 println ! ( " 필드 1 (0x00): 0x{:08X} ({})" , field1, field1) ;
112112 println ! ( " 필드 2 (0x04): 0x{:08X} ({})" , field2, field2) ;
113113 println ! ( " 필드 3 (0x08): 0x{:08X} ({}) - 높이?" , field3, field3) ;
114114 println ! ( " 필드 4 (0x0C): 0x{:08X} ({}) - 높이?" , field4, field4) ;
115- println ! ( " 필드 5 (0x10): 0x{:08X} ({}) - 왼쪽 여백?" , field5, field5) ;
116- println ! ( " 필드 6 (0x14): 0x{:08X} ({}) - 위쪽 여백?" , field6, field6) ;
117- println ! ( " 필드 7 (0x18): 0x{:08X} ({}) - 오른쪽 여백?" , field7, field7) ;
118- println ! ( " 필드 8 (0x1C): 0x{:08X} ({}) - 아래쪽 여백?" , field8, field8) ;
115+ println ! (
116+ " 필드 5 (0x10): 0x{:08X} ({}) - 왼쪽 여백?" ,
117+ field5, field5
118+ ) ;
119+ println ! (
120+ " 필드 6 (0x14): 0x{:08X} ({}) - 위쪽 여백?" ,
121+ field6, field6
122+ ) ;
123+ println ! (
124+ " 필드 7 (0x18): 0x{:08X} ({}) - 오른쪽 여백?" ,
125+ field7, field7
126+ ) ;
127+ println ! (
128+ " 필드 8 (0x1C): 0x{:08X} ({}) - 아래쪽 여백?" ,
129+ field8, field8
130+ ) ;
119131 println ! ( " 필드 9 (0x20): 0x{:08X} ({})" , field9, field9) ;
120132 println ! ( " 필드 10 (0x24): 0x{:08X} ({})" , field10, field10) ;
121-
133+
122134 // Convert HWPU to mm for better understanding
123135 println ! ( ) ;
124136 println ! ( " HWPU → mm 변환 (1 HWPU ≈ 0.01 mm):" ) ;
@@ -129,41 +141,41 @@ fn analyze_header_footer_detailed(record: &Record, position: u64) {
129141 println ! ( " 아래쪽 여백: {} mm" , field8 as f32 / 100.0 ) ;
130142 }
131143 }
132-
144+
133145 // Check if there are more bytes
134146 if record. data . len ( ) > 40 {
135147 println ! ( ) ;
136148 println ! ( "📝 추가 데이터 ({} bytes):" , record. data. len( ) - 40 ) ;
137149 let extra_data = & record. data [ 40 ..] ;
138150 print_hex_dump ( extra_data, 4 ) ;
139-
151+
140152 // Try to parse as text
141153 if extra_data. len ( ) >= 2 {
142154 println ! ( ) ;
143155 println ! ( "🔤 텍스트 데이터 시도:" ) ;
144-
156+
145157 // Try UTF-16LE
146158 if extra_data. len ( ) % 2 == 0 {
147159 let mut utf16_chars = Vec :: new ( ) ;
148160 for chunk in extra_data. chunks_exact ( 2 ) {
149161 let char_value = u16:: from_le_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ;
150162 utf16_chars. push ( char_value) ;
151163 }
152-
164+
153165 if let Ok ( text) = String :: from_utf16 ( & utf16_chars) {
154166 let clean_text = text. trim_end_matches ( '\0' ) ;
155167 if !clean_text. is_empty ( ) {
156168 println ! ( " UTF-16LE 해석: \" {}\" " , clean_text) ;
157169 }
158170 }
159171 }
160-
172+
161173 // Try as length-prefixed UTF-16LE
162174 if extra_data. len ( ) >= 2 {
163175 let mut reader = StreamReader :: new ( extra_data. to_vec ( ) ) ;
164176 if let Ok ( text_len) = reader. read_u16 ( ) {
165177 println ! ( " 길이 프리픽스: {} 문자" , text_len) ;
166-
178+
167179 let expected_bytes = text_len as usize * 2 ;
168180 if reader. remaining ( ) >= expected_bytes {
169181 if let Ok ( text_bytes) = reader. read_bytes ( expected_bytes) {
@@ -172,7 +184,7 @@ fn analyze_header_footer_detailed(record: &Record, position: u64) {
172184 let char_value = u16:: from_le_bytes ( [ chunk[ 0 ] , chunk[ 1 ] ] ) ;
173185 utf16_chars. push ( char_value) ;
174186 }
175-
187+
176188 if let Ok ( text) = String :: from_utf16 ( & utf16_chars) {
177189 println ! ( " 길이 기반 UTF-16LE: \" {}\" " , text) ;
178190 }
@@ -207,4 +219,4 @@ fn print_hex_dump(data: &[u8], max_lines: usize) {
207219 if data. len ( ) > max_lines * 16 {
208220 println ! ( " ... ({} more bytes)" , data. len( ) - max_lines * 16 ) ;
209221 }
210- }
222+ }
0 commit comments