-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathRetroManagerDatabase.py
More file actions
215 lines (181 loc) · 7.8 KB
/
RetroManagerDatabase.py
File metadata and controls
215 lines (181 loc) · 7.8 KB
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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
import sqlite3
import logging
import hashlib
class RetroManagerDatabase():
def __init__(self, location):
super().__init__()
# Connect to the database, if it doesnt exist this call will instantiate one
logging.info(f"Creating or opening {location}")
self.db = sqlite3.connect(location)
self.dbcursor = self.db.cursor()
#Validate the required tables exist
# TODO need to validate that the tables have the correct columns as well.
res = self.dbcursor.execute("SELECT name FROM sqlite_master where name = 'developer'")
if (res.fetchone() is None):
logging.info("RMDB: (developer) table not found, creating one")
self.dbcursor.execute("CREATE TABLE developer(id INTEGER PRIMARY KEY, name TEXT)")
res = self.dbcursor.execute("SELECT name FROM sqlite_master where name = 'series'")
if (res.fetchone() is None):
logging.info("RMDB: series table not found, creating one")
self.dbcursor.execute("CREATE TABLE series(id INTEGER PRIMARY KEY, name TEXT)")
res = self.dbcursor.execute("SELECT name FROM sqlite_master where name = 'games'")
if (res.fetchone() is None):
logging.info("RMDB: (games) table not found, creating one")
self.dbcursor.execute("""
CREATE TABLE games( id INTEGER PRIMARY KEY,
title TEXT,
filepath TEXT,
console,
developerid,
seriesid,
rating INTEGER CHECK(rating >=0 AND rating <=100),
date TIMESTAMP,
sha256hash TEXT,
FOREIGN KEY(developerid) REFERENCES developer(id),
FOREIGN KEY(seriesid) REFERENCES series(id)
)""")
res = self.dbcursor.execute("SELECT name FROM sqlite_master where name = 'saves'")
if (res.fetchone() is None):
logging.info("RMDB: (saves) table not found, creating one")
self.dbcursor.execute("""
CREATE TABLE saves( id INTEGER PRIMARY KEY,
gameid,
title TEXT,
filepath TEXT,
notes TEXT,
date TIMESTAMP,
hash TEXT,
FOREIGN KEY(gameid) REFERENCES games(id)
)""")
def validateTableGames(self):
self.ensureColumnIsInTable("games","sha256hash","TEXT")
def ensureColumnIsInTable(self, table, column, data_type):
# TODO check table exists
# TODO check if column already exists rather than assuming ALTER wont break anything
try:
self.dbcursor.execute(f"ALTER TABLE {table} ADD {column} {data_type}")
except Exception as e:
logging.info(f"RMDB~ValidateTableGames: not adding column ({column}) to ({table})")
"""
This function retrieves a list of games from the RetroManagerDatabase.
A filter string can be supplied, which currently does nothing.
If no filter is supplied then all games are returned.
Returns: a list of rmGame objects representing the games in the database
"""
def fetchGames(self, filter=""):
print(f"pulling games using filter ({filter})")
result = self.dbcursor.execute("SELECT id, title, filepath, console FROM games ORDER BY title")
resultsList = []
for row in result:
#print(row)
game = rmGame(row[0], row[1], row[2], row[3])
resultsList.append(game)
print(f"Found {len(resultsList)} games")
return resultsList
def fetchGameByTitle(self, title :str):
print(f"Searching for game by title ({title})")
result = self.dbcursor.execute("SELECT id, title, filepath, console FROM games WHERE title=?", title)
result = result.fetchone()
if not result is None:
game = rmGame(result[0], result[1], result[2], result[3])
return game
else:
return None
def addGame(self, title="", filepath="", console="", rating=-1):
try:
print(f"RMDB~Adding game: {title}")
self.dbcursor.execute("INSERT INTO games(title, filepath, console) values (?,?,?)",(title, filepath, console))
self.db.commit()
return True
except Exception as e:
print(f"error while adding game: {(e)}")
return False
def updateGame(self, game):
try:
if game.dbID == -1:
print(f"RMDB~updateGame invalid databaseID passed in")
return False
print(f"RMDB~Updating game: {game.title}")
self.dbcursor.execute("UPDATE games SET title = ?, filepath = ?, console = ? WHERE id = ?",(game.title, game.filePath, game.console, game.dbID))
self.db.commit()
return True
except Exception as e:
print(f"error while updating game: {(e)}")
return False
"""
Delete Games
TODO: Need to make sure that we delete any linked saves
"""
def deleteGame(self, game):
try:
if game.dbID == -1:
logging.error(f"RMDB~deleteGame: invalid databaseID passed in")
return False
logging.info(f"RMDB~deleteGame: {game.title}")
self.dbcursor.execute("DELETE FROM games WHERE id = ?",(game.dbID))
self.db.commit()
return True
except Exception as e:
logging.error(f"error while deleting game: {(e)}")
return False
"""
Create Save
"""
def createSave(self, gameID=-1, title="", filepath="", notes="", date=-1, hash =""):
try:
logging.info(f"RMDB~createSave: {title}")
self.dbcursor.execute("INSERT INTO saves(gameid, title, filepath, notes, date, hash) values (?,?,?,?,?,?)",(gameID, title, filepath, notes, date, hash))
self.db.commit()
return True
except Exception as e:
logging.error(f"RMDB~createSave: EXCEPTION {(e)}")
return False
"""
Get Save by ID
"""
"""
Get All Saves for Game
"""
"""
Update Save
"""
"""
Delete Save
"""
def addSeries(self, name):
print(f"Adding series: {name}")
def createFavouritesList(self, title : str):
print(f"Creating new Favourites list: {title}")
class rmGame():
dbID = -1
title = ""
filePath = ""
console = ""
series = ""
publisher = ""
rating = ""
def __init__(self, gameID, gameTitle, gameFilePath, gameConsole):
super().__init__()
self.dbID = gameID
self.title = gameTitle
self.filePath = gameFilePath
self.console = gameConsole
class rmSave():
def __init__(self, gameID, saveFilePath, saveFormat=""):
super().__init__()
self.gameDBID = gameID
self.filePath = saveFilePath
self.saveFormat = saveFormat
self.hash = ""
try:
BUF_SIZE = 4194304 # Reading in chunks of 4MB. There is no reason that this has to be this value, we can change it to anything.
sha256 = hashlib.sha256()
with open(saveFilePath, 'rb') as f:
while True:
data = f.read(BUF_SIZE)
if not data:
break
sha256.update(data)
self.hash = sha256.hexdigest()
except Exception as e:
logging.error(f"RetroManageDatabase~rmSave: {str(e)}")