| 1 | /********************************************************************** |
| 2 | * |
| 3 | * File scanner |
| 4 | * |
| 5 | * Copyright (c) 2021 Dario Deledda. All rights reserved. |
| 6 | * Use of this source code is governed by an MIT license |
| 7 | * that can be found in the LICENSE file. |
| 8 | * |
| 9 | * TODO: |
| 10 | **********************************************************************/ |
| 11 | import os |
| 12 | |
| 13 | // STBI supported format |
| 14 | // STBI_NO_JPEG * |
| 15 | // STBI_NO_PNG * |
| 16 | // STBI_NO_BMP * |
| 17 | // STBI_NO_PSD |
| 18 | // STBI_NO_TGA * |
| 19 | // STBI_NO_GIF * |
| 20 | // STBI_NO_HDR * |
| 21 | // STBI_NO_PIC * |
| 22 | // STBI_NO_PNM * |
| 23 | |
| 24 | /****************************************************************************** |
| 25 | * |
| 26 | * Struct and Enums |
| 27 | * |
| 28 | ******************************************************************************/ |
| 29 | enum Item_type { |
| 30 | file = 0 |
| 31 | folder |
| 32 | // archive format |
| 33 | zip = 16 |
| 34 | archive_file |
| 35 | // graphic format, MUST stay after the other types!! |
| 36 | bmp = 32 |
| 37 | jpg |
| 38 | png |
| 39 | gif |
| 40 | tga |
| 41 | ppm |
| 42 | pgm |
| 43 | pic |
| 44 | hdr |
| 45 | } |
| 46 | |
| 47 | pub struct Item { |
| 48 | pub mut: |
| 49 | path string |
| 50 | name string |
| 51 | size u64 |
| 52 | i_type Item_type = .file |
| 53 | container_index int // used if the item is in a container (.zip, .rar, etc) |
| 54 | container_item_index int // index in the container if the item is contained |
| 55 | need_extract bool // if true need to extraction from the container |
| 56 | drawable bool // if true the image can be showed |
| 57 | n_item int // |
| 58 | rotation int // number of rotation of PI/2 |
| 59 | } |
| 60 | |
| 61 | struct Item_list { |
| 62 | pub mut: |
| 63 | lst []Item |
| 64 | path_sep string |
| 65 | item_index int = -1 // image currently shown |
| 66 | n_item int // number of images scanned |
| 67 | loaded bool // flag that indicate that the list is ready to be used |
| 68 | } |
| 69 | |
| 70 | /****************************************************************************** |
| 71 | * |
| 72 | * Utility functions |
| 73 | * |
| 74 | ******************************************************************************/ |
| 75 | @[inline] |
| 76 | fn modulo(x int, n int) int { |
| 77 | return (x % n + n) % n |
| 78 | } |
| 79 | |
| 80 | @[inline] |
| 81 | fn get_extension(x string) Item_type { |
| 82 | // 4 char extension check |
| 83 | if x.len > 4 { |
| 84 | ext4 := x[x.len - 4..].to_lower() |
| 85 | match ext4 { |
| 86 | // containers |
| 87 | '.zip' { return .zip } |
| 88 | // graphic formats |
| 89 | '.jpg' { return .jpg } |
| 90 | '.png' { return .png } |
| 91 | '.bmp' { return .bmp } |
| 92 | '.gif' { return .gif } |
| 93 | '.tga' { return .tga } |
| 94 | '.ppm' { return .ppm } |
| 95 | '.pgm' { return .pgm } |
| 96 | '.pic' { return .pic } |
| 97 | '.hdr' { return .hdr } |
| 98 | else {} |
| 99 | } |
| 100 | } |
| 101 | // 5 char extension check |
| 102 | if x.len > 5 { |
| 103 | ext5 := x[x.len - 5..].to_lower() |
| 104 | if ext5 == '.jpeg' { |
| 105 | { |
| 106 | return .jpg |
| 107 | } |
| 108 | } |
| 109 | } |
| 110 | return .file |
| 111 | } |
| 112 | |
| 113 | @[inline] |
| 114 | fn is_image(x Item_type) bool { |
| 115 | if int(x) >= int(Item_type.bmp) { |
| 116 | return true |
| 117 | } |
| 118 | return false |
| 119 | } |
| 120 | |
| 121 | @[inline] |
| 122 | fn is_container(x Item_type) bool { |
| 123 | if x in [.zip, .folder] { |
| 124 | return true |
| 125 | } |
| 126 | return false |
| 127 | } |
| 128 | |
| 129 | @[inline] |
| 130 | fn (item_list Item_list) is_inside_a_container() bool { |
| 131 | if item_list.lst.len <= 0 || item_list.n_item <= 0 { |
| 132 | return false |
| 133 | } |
| 134 | return item_list.lst[item_list.item_index].need_extract |
| 135 | } |
| 136 | |
| 137 | @[inline] |
| 138 | fn (item_list Item_list) get_file_path() string { |
| 139 | if item_list.lst.len <= 0 || item_list.n_item <= 0 { |
| 140 | return '' |
| 141 | } |
| 142 | if item_list.lst[item_list.item_index].path != '' { |
| 143 | return '${item_list.lst[item_list.item_index].path}${item_list.path_sep}${item_list.lst[item_list.item_index].name}' |
| 144 | } |
| 145 | return item_list.lst[item_list.item_index].name |
| 146 | } |
| 147 | |
| 148 | /****************************************************************************** |
| 149 | * |
| 150 | * Scan functions |
| 151 | * |
| 152 | ******************************************************************************/ |
| 153 | fn (mut item_list Item_list) scan_folder(path string, in_index int) ! { |
| 154 | println('Scanning [${path}]') |
| 155 | mut folder_list := []string{} |
| 156 | lst := os.ls(path)! |
| 157 | |
| 158 | // manage the single files |
| 159 | for c, x in lst { |
| 160 | pt := '${path}${item_list.path_sep}${x}' |
| 161 | mut item := Item{ |
| 162 | path: path |
| 163 | name: x |
| 164 | container_index: in_index |
| 165 | container_item_index: c |
| 166 | } |
| 167 | if os.is_dir(pt) { |
| 168 | folder_list << x |
| 169 | } else { |
| 170 | ext := get_extension(x) |
| 171 | if ext == .zip { |
| 172 | item.i_type = .zip |
| 173 | item_list.lst << item |
| 174 | item_list.scan_zip(pt, item_list.lst.len - 1)! |
| 175 | continue |
| 176 | } |
| 177 | if is_image(ext) == true { |
| 178 | item_list.n_item += 1 |
| 179 | item.n_item = item_list.n_item |
| 180 | item.i_type = ext |
| 181 | item.drawable = true |
| 182 | item_list.lst << item |
| 183 | continue |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // manage the folders |
| 189 | for x in folder_list { |
| 190 | pt := '${path}${item_list.path_sep}${x}' |
| 191 | item := Item{ |
| 192 | path: path |
| 193 | name: x |
| 194 | i_type: .folder |
| 195 | } |
| 196 | item_list.lst << item |
| 197 | item_list.scan_folder(pt, item_list.lst.len - 1)! |
| 198 | } |
| 199 | // println(item_list.lst.len) |
| 200 | // println("==================================") |
| 201 | } |
| 202 | |
| 203 | fn (item_list Item_list) print_list() { |
| 204 | println('================================') |
| 205 | for x in item_list.lst { |
| 206 | if x.i_type == .folder { |
| 207 | print('[]') |
| 208 | } |
| 209 | if x.i_type == .zip { |
| 210 | print('[ZIP]') |
| 211 | } |
| 212 | println('${x.path} => ${x.container_index} ${x.container_item_index} ${x.name} ne:${x.need_extract}') |
| 213 | } |
| 214 | println('n_item: ${item_list.n_item} index: ${item_list.item_index}') |
| 215 | println('================================') |
| 216 | } |
| 217 | |
| 218 | fn (mut item_list Item_list) get_items_list(args []string) { |
| 219 | item_list.loaded = false |
| 220 | println('Args: ${args}') |
| 221 | |
| 222 | item_list.path_sep = $if windows { '\\' } $else { '/' } |
| 223 | for x in args { |
| 224 | // scan folder |
| 225 | if os.is_dir(x) { |
| 226 | mut item := Item{ |
| 227 | path: x |
| 228 | name: x |
| 229 | container_index: item_list.lst.len |
| 230 | i_type: .folder |
| 231 | } |
| 232 | item_list.lst << item |
| 233 | item_list.scan_folder(x, item_list.lst.len - 1) or { |
| 234 | eprintln('ERROR: scanning folder [${x}]!') |
| 235 | continue |
| 236 | } |
| 237 | } else { |
| 238 | mut item := Item{ |
| 239 | path: '' |
| 240 | name: x |
| 241 | container_index: -1 |
| 242 | } |
| 243 | ext := get_extension(x) |
| 244 | // scan .zip |
| 245 | if ext == .zip { |
| 246 | item.i_type = .zip |
| 247 | item_list.lst << item |
| 248 | item_list.scan_zip(x, item_list.lst.len - 1) or { |
| 249 | eprintln('ERROR: scanning zip [${x}]!') |
| 250 | continue |
| 251 | } |
| 252 | continue |
| 253 | } |
| 254 | // single images |
| 255 | if is_image(ext) == true { |
| 256 | item_list.n_item += 1 |
| 257 | item.n_item = item_list.n_item |
| 258 | item.i_type = ext |
| 259 | item.drawable = true |
| 260 | item_list.lst << item |
| 261 | continue |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | // debug call for list all the loaded items |
| 266 | // item_list.print_list() |
| 267 | |
| 268 | println('Items: ${item_list.n_item}') |
| 269 | println('Scanning done.') |
| 270 | |
| 271 | item_list.get_next_item(1) |
| 272 | item_list.loaded = true |
| 273 | } |
| 274 | |
| 275 | /****************************************************************************** |
| 276 | * |
| 277 | * Navigation functions |
| 278 | * |
| 279 | ******************************************************************************/ |
| 280 | fn (mut item_list Item_list) get_next_item(in_inc int) { |
| 281 | // if empty exit |
| 282 | if item_list.lst.len <= 0 || item_list.n_item <= 0 { |
| 283 | return |
| 284 | } |
| 285 | |
| 286 | inc := if in_inc > 0 { 1 } else { -1 } |
| 287 | mut i := item_list.item_index + in_inc |
| 288 | i = modulo(i, item_list.lst.len) |
| 289 | start := i |
| 290 | for { |
| 291 | if item_list.lst[i].drawable == true { |
| 292 | item_list.item_index = i |
| 293 | break |
| 294 | } |
| 295 | i = i + inc |
| 296 | i = modulo(i, item_list.lst.len) |
| 297 | // if we are in a loop break it |
| 298 | if i == start { |
| 299 | break |
| 300 | } |
| 301 | } |
| 302 | // println("Found: ${item_list.item_index}") |
| 303 | } |
| 304 | |
| 305 | fn (mut item_list Item_list) go_to_next_container(in_inc int) { |
| 306 | // if empty exit |
| 307 | if item_list.lst.len <= 0 || item_list.n_item <= 0 { |
| 308 | return |
| 309 | } |
| 310 | inc := if in_inc > 0 { 1 } else { -1 } |
| 311 | mut i := item_list.item_index + in_inc |
| 312 | i = modulo(i, item_list.lst.len) |
| 313 | start := i |
| 314 | for { |
| 315 | // check if we found a folder |
| 316 | if is_container(item_list.lst[i].i_type) == true |
| 317 | && i != item_list.lst[item_list.item_index].container_index { |
| 318 | item_list.item_index = i |
| 319 | item_list.get_next_item(1) |
| 320 | break |
| 321 | } |
| 322 | // continue to search |
| 323 | i = i + inc |
| 324 | i = modulo(i, item_list.lst.len) |
| 325 | // if we are in a loop break it |
| 326 | if i == start { |
| 327 | break |
| 328 | } |
| 329 | } |
| 330 | } |
| 331 | |
| 332 | /****************************************************************************** |
| 333 | * |
| 334 | * Other functions |
| 335 | * |
| 336 | ******************************************************************************/ |
| 337 | @[inline] |
| 338 | fn (mut item_list Item_list) rotate(in_inc int) { |
| 339 | item_list.lst[item_list.item_index].rotation += in_inc |
| 340 | if item_list.lst[item_list.item_index].rotation >= 4 { |
| 341 | item_list.lst[item_list.item_index].rotation = 0 |
| 342 | } |
| 343 | } |
| 344 | |