mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-14 16:27:13 +03:00
Point to journal location in SavedGames folder
This commit is contained in:
parent
78e001343c
commit
91b5d0a6ea
@ -109,8 +109,8 @@
|
||||
/* Empire rank. [stats.py] */
|
||||
"Duke" = "Duke";
|
||||
|
||||
/* Configuration setting. [prefs.py] */
|
||||
"E:D log file location" = "E:D log file location";
|
||||
/* Location of the new Journal file in E:D 2.2. [prefs.py] */
|
||||
"E:D journal file location" = "E:D journal file location";
|
||||
|
||||
/* Empire rank. [stats.py] */
|
||||
"Earl" = "Earl";
|
||||
|
10
README.md
10
README.md
@ -69,7 +69,7 @@ This app can save a variety of data in a variety of formats:
|
||||
|
||||
By default these files will be placed in your Documents folder. Since this app will create a lot of files if you use it for a while you may wish to create a separate folder for the files and tell the app to place them there.
|
||||
|
||||
Some options work by reading the Elite: Dangerous game's log files. Normally this app will find the log files but if you find some options greyed-out then adjust the “E:D log file location” setting described [below](#doesnt-track-systems-visited).
|
||||
Some options work by reading the Elite: Dangerous game's “journal” files. If you're running this app on a different machine from the Elite: Dangerous game then adjust the “E:D journal file location” setting on the Configuration tab to point to the game's journal files.
|
||||
|
||||
### EDDN
|
||||
|
||||
@ -126,13 +126,7 @@ Ensure that you visit the in-game Commodity Market at a station where you intend
|
||||
This problem is tracked as [Issue #92](https://github.com/Marginal/EDMarketConnector/issues/92).
|
||||
|
||||
### Doesn't track Systems visited
|
||||
This app uses Elite: Dangerous' log files to track the systems and stations that you visit. When looking for the log files, this app assumes:
|
||||
|
||||
- That you're running this app and Elite: Dangerous on the same machine.
|
||||
- That you're running Elite: Dangerous from Steam, if you have both Steam and non-Steam versions installed.
|
||||
- That you're running “Horizons” 64bit, if you have both Horizons and Season 1 installed.
|
||||
|
||||
If you find that this app isn't automatically tracking the systems that you visit and/or isn't automatically “updating” on docking (if you have that option selected), or if you're running this app on a different machine from the Elite: Dangerous game then adjust the “E:D log file location” setting on the Configuration tab to point to the game's log files using [this info](https://support.frontier.co.uk/kb/faq.php?id=108) as a guide.
|
||||
This app uses Elite: Dangerous' “journal” files to track the systems and stations that you visit. If you're running this app on a different machine from the Elite: Dangerous game, or if you find that this app isn't automatically tracking the systems that you visit and/or isn't automatically “updating” on docking (if you have that option selected), then adjust the “E:D journal file location” setting on the Configuration tab to point to the game's journal files.
|
||||
|
||||
Running from source
|
||||
--------
|
||||
|
38
config.py
38
config.py
@ -1,7 +1,7 @@
|
||||
import numbers
|
||||
import sys
|
||||
from os import getenv, makedirs, mkdir, pardir
|
||||
from os.path import expanduser, dirname, isdir, join, normpath
|
||||
from os.path import expanduser, dirname, exists, isdir, join, normpath
|
||||
from sys import platform
|
||||
|
||||
|
||||
@ -19,14 +19,21 @@ if platform=='darwin':
|
||||
|
||||
elif platform=='win32':
|
||||
import ctypes
|
||||
from ctypes.wintypes import *
|
||||
import uuid
|
||||
|
||||
CSIDL_PERSONAL = 0x0005
|
||||
CSIDL_LOCAL_APPDATA = 0x001C
|
||||
CSIDL_PROFILE = 0x0028
|
||||
FOLDERID_Documents = uuid.UUID('{FDD39AD0-238F-46AF-ADB4-6C85480369C7}')
|
||||
FOLDERID_LocalAppData = uuid.UUID('{F1B32785-6FBA-4FCF-9D55-7B8E7F157091}')
|
||||
FOLDERID_Profile = uuid.UUID('{5E6C858F-0E22-4760-9AFE-EA3317B67173}')
|
||||
FOLDERID_SavedGames = uuid.UUID('{4C5C32FF-BB9D-43b0-B5B4-2D72E54EAAA4}')
|
||||
|
||||
SHGetKnownFolderPath = ctypes.windll.shell32.SHGetKnownFolderPath
|
||||
SHGetKnownFolderPath.argtypes = [ctypes.c_char_p, DWORD, HANDLE, ctypes.POINTER(ctypes.c_wchar_p)]
|
||||
|
||||
CoTaskMemFree = ctypes.windll.ole32.CoTaskMemFree
|
||||
CoTaskMemFree.argtypes = [ctypes.c_void_p]
|
||||
|
||||
# _winreg that ships with Python 2 doesn't support unicode, so do this instead
|
||||
from ctypes.wintypes import *
|
||||
|
||||
HKEY_CURRENT_USER = 0x80000001
|
||||
KEY_ALL_ACCESS = 0x000F003F
|
||||
REG_CREATED_NEW_KEY = 0x00000001
|
||||
@ -100,6 +107,8 @@ class Config:
|
||||
if not isdir(self.plugin_dir):
|
||||
mkdir(self.plugin_dir)
|
||||
|
||||
self.default_journal_dir = join(NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)[0], 'Frontier Developments', 'Elite Dangerous', 'Logs') # FIXME: check this
|
||||
|
||||
self.home = expanduser('~')
|
||||
|
||||
self.respath = getattr(sys, 'frozen', False) and normpath(join(dirname(sys.executable), pardir, 'Resources')) or dirname(__file__)
|
||||
@ -148,19 +157,25 @@ class Config:
|
||||
|
||||
def __init__(self):
|
||||
|
||||
buf = ctypes.create_unicode_buffer(MAX_PATH)
|
||||
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, buf, CSIDL_LOCAL_APPDATA, 0)
|
||||
buf = ctypes.c_wchar_p()
|
||||
SHGetKnownFolderPath(ctypes.create_string_buffer(FOLDERID_LocalAppData.bytes_le), 0, 0, ctypes.byref(buf))
|
||||
self.app_dir = join(buf.value, appname)
|
||||
if not isdir(self.app_dir):
|
||||
mkdir(self.app_dir)
|
||||
CoTaskMemFree(buf)
|
||||
|
||||
self.plugin_dir = join(self.app_dir, 'plugins')
|
||||
if not isdir(self.plugin_dir):
|
||||
mkdir(self.plugin_dir)
|
||||
|
||||
# expanduser in Python 2 on Windows doesn't handle non-ASCII - http://bugs.python.org/issue13207
|
||||
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, buf, CSIDL_PROFILE, 0)
|
||||
SHGetKnownFolderPath(ctypes.create_string_buffer(FOLDERID_Profile.bytes_le), 0, 0, ctypes.byref(buf))
|
||||
self.home = buf.value
|
||||
CoTaskMemFree(buf)
|
||||
|
||||
SHGetKnownFolderPath(ctypes.create_string_buffer(FOLDERID_SavedGames.bytes_le), 0, 0, ctypes.byref(buf))
|
||||
self.default_journal_dir = buf.value
|
||||
CoTaskMemFree(buf)
|
||||
|
||||
self.respath = dirname(getattr(sys, 'frozen', False) and sys.executable or __file__)
|
||||
|
||||
@ -190,8 +205,9 @@ class Config:
|
||||
|
||||
if not self.get('outdir') or not isdir(self.get('outdir')):
|
||||
buf = ctypes.create_unicode_buffer(MAX_PATH)
|
||||
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, buf, CSIDL_PERSONAL, 0)
|
||||
SHGetKnownFolderPath(ctypes.create_string_buffer(FOLDERID_Documents.bytes_le), 0, 0, ctypes.byref(buf))
|
||||
self.set('outdir', buf.value)
|
||||
CoTaskMemFree(buf)
|
||||
|
||||
def get(self, key):
|
||||
typ = DWORD()
|
||||
@ -253,6 +269,8 @@ class Config:
|
||||
if not isdir(self.plugin_dir):
|
||||
mkdir(self.plugin_dir)
|
||||
|
||||
self.default_journal_dir = None
|
||||
|
||||
self.home = expanduser('~')
|
||||
|
||||
self.respath = dirname(__file__)
|
||||
|
115
monitor.py
115
monitor.py
@ -65,7 +65,6 @@ class EDLogs(FileSystemEventHandler):
|
||||
def __init__(self):
|
||||
FileSystemEventHandler.__init__(self) # futureproofing - not need for current version of watchdog
|
||||
self.root = None
|
||||
self.logdir = self._logdir() # E:D client's default Logs directory, or None if not found
|
||||
self.currentdir = None # The actual logdir that we're monitoring
|
||||
self.logfile = None
|
||||
self.observer = None
|
||||
@ -88,8 +87,8 @@ class EDLogs(FileSystemEventHandler):
|
||||
|
||||
def start(self, root):
|
||||
self.root = root
|
||||
logdir = config.get('logdir') or self.logdir
|
||||
if not self.is_valid_logdir(logdir):
|
||||
logdir = config.get('journaldir') or config.default_journal_dir
|
||||
if not logdir or not exists(logdir):
|
||||
self.stop()
|
||||
return False
|
||||
|
||||
@ -101,7 +100,7 @@ class EDLogs(FileSystemEventHandler):
|
||||
# File system events are unreliable/non-existent over network drives on Linux.
|
||||
# We can't easily tell whether a path points to a network drive, so assume
|
||||
# any non-standard logdir might be on a network drive and poll instead.
|
||||
polling = bool(config.get('logdir')) and platform != 'win32'
|
||||
polling = bool(config.get('journaldir')) and platform != 'win32'
|
||||
if not polling and not self.observer:
|
||||
self.observer = Observer()
|
||||
self.observer.daemon = True
|
||||
@ -239,114 +238,6 @@ class EDLogs(FileSystemEventHandler):
|
||||
else:
|
||||
return self.parse_entry(self.event_queue.pop(0))
|
||||
|
||||
def is_valid_logdir(self, path):
|
||||
return self._is_valid_logdir(path)
|
||||
|
||||
|
||||
if platform=='darwin':
|
||||
|
||||
def _logdir(self):
|
||||
# https://support.frontier.co.uk/kb/faq.php?id=97
|
||||
paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, True)
|
||||
if len(paths) and self._is_valid_logdir(join(paths[0], 'Frontier Developments', 'Elite Dangerous', 'Logs')):
|
||||
return join(paths[0], 'Frontier Developments', 'Elite Dangerous', 'Logs')
|
||||
else:
|
||||
return None
|
||||
|
||||
def _is_valid_logdir(self, path):
|
||||
# Apple's SMB implementation is too flaky so assume target machine is OSX
|
||||
return path and isdir(path) and isfile(join(path, pardir, 'AppNetCfg.xml'))
|
||||
|
||||
|
||||
elif platform=='win32':
|
||||
|
||||
def _logdir(self):
|
||||
|
||||
# Try locations described in https://support.elitedangerous.com/kb/faq.php?id=108, in reverse order of age
|
||||
candidates = []
|
||||
|
||||
# Steam and Steam libraries
|
||||
key = HKEY()
|
||||
if not RegOpenKeyEx(HKEY_CURRENT_USER, r'Software\Valve\Steam', 0, KEY_READ, ctypes.byref(key)):
|
||||
valtype = DWORD()
|
||||
valsize = DWORD()
|
||||
if not RegQueryValueEx(key, 'SteamPath', 0, ctypes.byref(valtype), None, ctypes.byref(valsize)) and valtype.value == REG_SZ:
|
||||
buf = ctypes.create_unicode_buffer(valsize.value / 2)
|
||||
if not RegQueryValueEx(key, 'SteamPath', 0, ctypes.byref(valtype), buf, ctypes.byref(valsize)):
|
||||
steampath = buf.value.replace('/', '\\') # For some reason uses POSIX seperators
|
||||
steamlibs = [steampath]
|
||||
try:
|
||||
# Simple-minded Valve VDF parser
|
||||
with open(join(steampath, 'config', 'config.vdf'), 'rU') as h:
|
||||
for line in h:
|
||||
vals = line.split()
|
||||
if vals and vals[0].startswith('"BaseInstallFolder_'):
|
||||
steamlibs.append(vals[1].strip('"').replace('\\\\', '\\'))
|
||||
except:
|
||||
pass
|
||||
for lib in steamlibs:
|
||||
candidates.append(join(lib, 'steamapps', 'common', 'Elite Dangerous', 'Products'))
|
||||
RegCloseKey(key)
|
||||
|
||||
# Next try custom installation under the Launcher
|
||||
if not RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
||||
machine().endswith('64') and
|
||||
r'SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall' or # Assumes that the launcher is a 32bit process
|
||||
r'SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall',
|
||||
0, KEY_READ, ctypes.byref(key)):
|
||||
buf = ctypes.create_unicode_buffer(MAX_PATH)
|
||||
i = 0
|
||||
while True:
|
||||
size = DWORD(MAX_PATH)
|
||||
if RegEnumKeyEx(key, i, buf, ctypes.byref(size), None, None, None, None):
|
||||
break
|
||||
|
||||
subkey = HKEY()
|
||||
if not RegOpenKeyEx(key, buf, 0, KEY_READ, ctypes.byref(subkey)):
|
||||
valtype = DWORD()
|
||||
valsize = DWORD((len('Frontier Developments')+1)*2)
|
||||
valbuf = ctypes.create_unicode_buffer(valsize.value / 2)
|
||||
if not RegQueryValueEx(subkey, 'Publisher', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)) and valtype.value == REG_SZ and valbuf.value == 'Frontier Developments':
|
||||
if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), None, ctypes.byref(valsize)) and valtype.value == REG_SZ:
|
||||
valbuf = ctypes.create_unicode_buffer(valsize.value / 2)
|
||||
if not RegQueryValueEx(subkey, 'InstallLocation', 0, ctypes.byref(valtype), valbuf, ctypes.byref(valsize)):
|
||||
candidates.append(join(valbuf.value, 'Products'))
|
||||
RegCloseKey(subkey)
|
||||
i += 1
|
||||
RegCloseKey(key)
|
||||
|
||||
# Standard non-Steam locations
|
||||
programs = ctypes.create_unicode_buffer(MAX_PATH)
|
||||
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, programs, CSIDL_PROGRAM_FILESX86, 0)
|
||||
candidates.append(join(programs.value, 'Frontier', 'Products')),
|
||||
|
||||
applocal = ctypes.create_unicode_buffer(MAX_PATH)
|
||||
ctypes.windll.shell32.SHGetSpecialFolderPathW(0, applocal, CSIDL_LOCAL_APPDATA, 0)
|
||||
candidates.append(join(applocal.value, 'Frontier_Developments', 'Products'))
|
||||
|
||||
for game in ['elite-dangerous-64', 'FORC-FDEV-D-1']: # Look for Horizons in all candidate places first
|
||||
for base in candidates:
|
||||
if isdir(base):
|
||||
for d in listdir(base):
|
||||
if d.startswith(game) and self._is_valid_logdir(join(base, d, 'Logs')):
|
||||
return join(base, d, 'Logs')
|
||||
|
||||
return None
|
||||
|
||||
def _is_valid_logdir(self, path):
|
||||
# Assume target machine is Windows
|
||||
return path and isdir(path) and isfile(join(path, pardir, 'AppConfig.xml'))
|
||||
|
||||
|
||||
elif platform=='linux2':
|
||||
|
||||
def _logdir(self):
|
||||
return None
|
||||
|
||||
def _is_valid_logdir(self, path):
|
||||
# Assume target machine is Windows
|
||||
return path and isdir(path) and isfile(join(path, pardir, 'AppConfig.xml'))
|
||||
|
||||
|
||||
# singleton
|
||||
monitor = EDLogs()
|
||||
|
30
prefs.py
30
prefs.py
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from os.path import dirname, expanduser, isdir, join, sep
|
||||
from os.path import dirname, expanduser, exists, isdir, join, sep
|
||||
from sys import platform
|
||||
|
||||
import Tkinter as tk
|
||||
@ -191,7 +191,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
configframe.columnconfigure(1, weight=1)
|
||||
|
||||
self.logdir = nb.Entry(configframe, takefocus=False)
|
||||
logdir = config.get('logdir') or monitor.logdir
|
||||
logdir = config.get('journaldir') or config.default_journal_dir
|
||||
if not logdir:
|
||||
pass
|
||||
elif logdir.startswith(config.home):
|
||||
@ -202,14 +202,14 @@ class PreferencesDialog(tk.Toplevel):
|
||||
|
||||
if platform != 'darwin':
|
||||
# Apple's SMB implementation is way too flaky - no filesystem events and bogus NULLs
|
||||
nb.Label(configframe, text = _('E:D log file location')+':').grid(columnspan=3, padx=PADX, sticky=tk.W) # Configuration setting
|
||||
nb.Label(configframe, text = _('E:D journal file location')+':').grid(columnspan=3, padx=PADX, sticky=tk.W) # Location of the new Journal file in E:D 2.2
|
||||
self.logdir.grid(row=10, columnspan=2, padx=(PADX,0), sticky=tk.EW)
|
||||
self.logbutton = nb.Button(configframe, text=(platform=='darwin' and _('Change...') or # Folder selection button on OSX
|
||||
_('Browse...')), # Folder selection button on Windows
|
||||
command = lambda:self.filebrowse(_('E:D log file location'), self.logdir))
|
||||
command = lambda:self.filebrowse(_('E:D journal file location'), self.logdir))
|
||||
self.logbutton.grid(row=10, column=2, padx=PADX, sticky=tk.EW)
|
||||
if monitor.logdir:
|
||||
nb.Button(configframe, text=_('Default'), command=self.logdir_reset, state = monitor.logdir and tk.NORMAL or tk.DISABLED).grid(column=2, padx=PADX, pady=(5,0), sticky=tk.EW) # Appearance theme and language setting
|
||||
if config.default_journal_dir:
|
||||
nb.Button(configframe, text=_('Default'), command=self.logdir_reset, state = config.get('journaldir') and tk.NORMAL or tk.DISABLED).grid(column=2, padx=PADX, pady=(5,0), sticky=tk.EW) # Appearance theme and language setting
|
||||
|
||||
if platform == 'win32':
|
||||
ttk.Separator(configframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW)
|
||||
@ -308,7 +308,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
|
||||
def outvarchanged(self):
|
||||
logdir = self.logdir.get().startswith('~') and join(config.home, self.logdir.get()[2:]) or self.logdir.get()
|
||||
logvalid = monitor.is_valid_logdir(logdir)
|
||||
logvalid = logdir and exists(logdir)
|
||||
|
||||
local = self.out_bpc.get() or self.out_td.get() or self.out_csv.get() or self.out_ship_eds.get() or self.out_ship_coriolis.get()
|
||||
self.out_auto_button['state'] = local and logvalid and tk.NORMAL or tk.DISABLED
|
||||
@ -366,12 +366,12 @@ class PreferencesDialog(tk.Toplevel):
|
||||
def logdir_reset(self):
|
||||
self.logdir['state'] = tk.NORMAL # must be writable to update
|
||||
self.logdir.delete(0, tk.END)
|
||||
if not monitor.logdir:
|
||||
pass
|
||||
elif monitor.logdir.startswith(config.home):
|
||||
self.logdir.insert(0, '~' + monitor.logdir[len(config.home):])
|
||||
if not config.default_journal_dir:
|
||||
pass # Can't reset
|
||||
elif config.default_journal_dir.startswith(config.home):
|
||||
self.logdir.insert(0, '~' + config.default_journal_dir[len(config.home):])
|
||||
else:
|
||||
self.logdir.insert(0, monitor.logdir)
|
||||
self.logdir.insert(0, config.default_journal_dir)
|
||||
self.logdir['state'] = 'readonly'
|
||||
self.outvarchanged()
|
||||
|
||||
@ -456,10 +456,10 @@ class PreferencesDialog(tk.Toplevel):
|
||||
config.set('edsm_apikey', self.edsm_apikey.get().strip())
|
||||
|
||||
logdir = self.logdir.get().startswith('~') and join(config.home, self.logdir.get()[2:]) or self.logdir.get()
|
||||
if monitor.logdir and logdir.lower() == monitor.logdir.lower():
|
||||
config.set('logdir', '') # default location
|
||||
if config.default_journal_dir and logdir.lower() == config.default_journal_dir.lower():
|
||||
config.set('journaldir', '') # default location
|
||||
else:
|
||||
config.set('logdir', logdir)
|
||||
config.set('journaldir', logdir)
|
||||
if platform in ['darwin','win32']:
|
||||
config.set('hotkey_code', self.hotkey_code)
|
||||
config.set('hotkey_mods', self.hotkey_mods)
|
||||
|
Loading…
x
Reference in New Issue
Block a user