-
Notifications
You must be signed in to change notification settings - Fork 67
/
scanner.go
100 lines (95 loc) · 3.08 KB
/
scanner.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// Package scanner generates game playlists by scanning your game collection
// against the database. It uses CRC checksums for No-Intro zip files and
// name matching for Redump cue files.
package scanner
import (
"archive/zip"
"hash/crc32"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
ntf "github.com/libretro/ludo/notifications"
"github.com/libretro/ludo/playlists"
"github.com/libretro/ludo/rdb"
"github.com/libretro/ludo/settings"
"github.com/libretro/ludo/state"
"github.com/libretro/ludo/utils"
"github.com/rs/xid"
)
// LoadDB loops over the RDBs in a given directory and parses them
func LoadDB(dir string) (rdb.DB, error) {
files, err := ioutil.ReadDir(dir)
if err != nil {
return rdb.DB{}, err
}
db := make(rdb.DB)
for _, f := range files {
name := f.Name()
if !strings.Contains(name, ".rdb") {
continue
}
system := name[0 : len(name)-4]
bytes, _ := ioutil.ReadFile(filepath.Join(dir, name))
db[system] = rdb.Parse(bytes)
}
return db, nil
}
// ScanDir scans a full directory, report progress and generate playlists
func ScanDir(dir string, doneCb func()) {
nid := ntf.DisplayAndLog(ntf.Info, "Menu", "Scanning %s", dir)
roms := utils.AllFilesIn(dir)
games := make(chan (rdb.Game))
go Scan(dir, roms, games, nid)
go func() {
for game := range games {
os.MkdirAll(settings.Current.PlaylistsDirectory, os.ModePerm)
CSVPath := filepath.Join(settings.Current.PlaylistsDirectory, game.System+".csv")
if playlists.Contains(CSVPath, game.Path, game.CRC32) {
continue
}
f, _ := os.OpenFile(CSVPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
f.WriteString(game.Path + "\t")
f.WriteString(game.Name + "\t")
if uint64(game.CRC32) > 0 {
f.WriteString(strconv.FormatUint(uint64(game.CRC32), 16))
}
f.WriteString("\n")
f.Close()
}
doneCb()
ntf.Update(nid, ntf.Success, "Done scanning.")
}()
}
// Scan scans a list of roms against the database
func Scan(dir string, roms []string, games chan (rdb.Game), nid xid.ID) {
for i, f := range roms {
ext := filepath.Ext(f)
switch ext {
case ".zip":
// Open the ZIP archive
z, _ := zip.OpenReader(f)
for _, rom := range z.File {
if rom.CRC32 > 0 {
// Look for a matching game entry in the database
state.Global.DB.FindByCRC(f, rom.Name, rom.CRC32, games)
ntf.Update(nid, ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f)
}
}
z.Close()
case ".cue":
// Look for a matching game entry in the database
state.Global.DB.FindByROMName(f, filepath.Base(f), 0, games)
ntf.Update(nid, ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f)
case ".32x", "a52", ".a78", ".col", ".crt", ".d64", ".pce", ".fds", ".gb", ".gba", ".gbc", ".gen", ".gg", ".ipf", ".j64", ".jag", ".lnx", ".md", ".n64", ".nes", ".ngc", ".nds", ".rom", ".sfc", ".sg", ".smd", ".sms", ".ws", ".wsc":
fd, _ := os.Open(f)
bytes, _ := ioutil.ReadAll(fd)
CRC32 := crc32.ChecksumIEEE(bytes)
state.Global.DB.FindByCRC(f, utils.FileName(f), CRC32, games)
ntf.Update(nid, ntf.Info, strconv.Itoa(i)+"/"+strconv.Itoa(len(roms))+" "+f)
fd.Close()
}
}
close(games)
}