1
0
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:
Jonathan Harris 2016-08-30 17:00:48 +01:00
parent 78e001343c
commit 91b5d0a6ea
5 changed files with 50 additions and 147 deletions

View File

@ -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";

View File

@ -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
--------

View File

@ -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__)

View File

@ -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()

View File

@ -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)