Skip to content
This repository has been archived by the owner on Sep 2, 2022. It is now read-only.

Commit

Permalink
First working version
Browse files Browse the repository at this point in the history
  • Loading branch information
RZetko committed Aug 18, 2019
1 parent 6a8e908 commit 7784444
Show file tree
Hide file tree
Showing 332 changed files with 129,568 additions and 349 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
.vscode
.vscode
__pycache__
47 changes: 46 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,46 @@
WIP
GOG Galaxy 2.0 Final Fantasy XIV integration

# Installation

Clone repository to `%localappdata%\GOG.com\Galaxy\plugins\installed\ffxiv`

# Working with code

## Before starting
Install Python extensions (shuold not be needed) `pip install -r requirements.txt -t ./modules --implementation cp --python-version 37 --only-binary=:all:`

## Files and folders
* ./html/
* folder with html files that will popup when first connecting integration
* ./modules/
* folder with installed python modules required for proper functionality of integration
* ./ffxiv_api.py
* handles logging in and retrieving character details
* ./ffxiv_localgame.py
* handles tasks with local game - starting, deleting
* ./ffxiv_tools.py
* helper functions
* ./plugin.py
* main script responsible for launching integration
* ./version.py
* contains current version of integration
* ./manifest.json
* contains identification info about integration
* ./requirements.txt
* contains python modules required for proper functionality of integration

# Changelog
* v. 1.0.0
* First working release
* Supports launching game, uninstalling game, detecting game launch and if it's running, synchronizing friends
* Installing game currently not supported - WIP
* Achievements syncing currently not supported - needs more research, may be unable to support because of platform limitations

# Thanks

[@gogcom](https://github.com/gogcom) for making GOG Galaxy 2 and giving me access to beta
https://github.com/gogcom/galaxy-integrations-python-api

[@Mixaill](https://github.com/Mixaill) for his GOG Galaxy Guild Wars 2 integration which I used as base for this integration. https://github.com/Mixaill/galaxy-integration-gw2

[@viion](https://twitter.com/viion) for A FINAL FANTASY XIV: Online REST API https://xivapi.com/
8 changes: 0 additions & 8 deletions ffxiv/consts.py

This file was deleted.

55 changes: 0 additions & 55 deletions ffxiv/localgame.py

This file was deleted.

9 changes: 0 additions & 9 deletions ffxiv/tools.py

This file was deleted.

68 changes: 50 additions & 18 deletions ffxiv/api.py → ffxiv_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@
import sys
import pprint
import threading
import modules.requests as requests
import tempfile
import modules.urllib3 as urllib3

from urllib.parse import parse_qs
from typing import Dict, List
from http.server import HTTPServer, BaseHTTPRequestHandler
from enum import Enum

modules = os.path.join(os.path.dirname(os.path.realpath(__file__)),'modules\\')

if modules not in sys.path:
sys.path.insert(0, modules)

import modules.requests as requests

class FFXIVAuthorizationResult(Enum):
FAILED = 0
FAILED_INVALID_CHARACTER_ID = 1
Expand Down Expand Up @@ -51,7 +59,7 @@ def do_POST_login(self, data):

if data_valid:
try:
auth_result = self.backend.do_auth_character(data[b'character_id'][0]).decode("utf-8")
auth_result = self.backend.do_auth_character(data[b'character_id'][0].decode("utf-8"))
except Exception:
logging.exception("error on doing auth:")

Expand All @@ -60,10 +68,8 @@ def do_POST_login(self, data):

if auth_result == FFXIVAuthorizationResult.FINISHED:
self.send_header('Location','/finished')
# elif auth_result == GW2AuthorizationResult.FAILED:
# self.send_header('Location', '/login_noaccount')
# else:
# self.send_header('Location','/login_failed')
else:
self.send_header('Location','/login_failed')

self.end_headers()

Expand Down Expand Up @@ -96,9 +102,10 @@ def do_GET(self):

class FFXIVAPI(object):
API_DOMAIN = 'https://xivapi.com/'
API_URL_CHARACTER = '/character'
API_URL_CHARACTER = 'character/'
LOCALSERVER_HOST = '127.0.0.1'
LOCALSERVER_PORT = 13338
INSTALL_URL = "http://gdl.square-enix.com/ffxiv/inst/ffxivsetup.exe"

def __init__(self):
self._server_thread = None
Expand All @@ -111,14 +118,19 @@ def __init__(self):
#

def get_character_id(self) -> str:
return self.character_id
return self._character_id

def get_character(self) -> List[str]:
return self._account_info['Character']

def get_character_name(self) -> str:
return self._account_info['Character']['Name']

def get_account_achievements(self) -> List[str]:
return self._account_info['achievements']
return self._account_info['Achievements']['List']

def get_account_friends(self) -> List[str]:
return self._account_info['friends']

return self._account_info['Friends']

#
# Authorization server
Expand All @@ -131,12 +143,10 @@ def auth_server_start(self) -> bool:

if self._server_thread is not None:
logging.warning('FFXIVAuthorization/auth_server_start: Auth server thread is already running')

return False

if self._server_object is not None:
logging.warning('FFXIVAuthorization/auth_server_start: Auth server object is exists')

return False

FFXIVAuthorizationServer.backend = self
Expand All @@ -153,15 +163,13 @@ def auth_server_stop(self) -> bool:
self._server_object = None
else:
logging.warning('FFXIVAuthorization/auth_server_stop: Auth server object is not exits')

return False

if self._server_thread is not None:
self._server_thread.join()
self._server_thread = None
else:
logging.warning('FFXIVAuthorization/auth_server_stop: Auth server thread is not running')

return False

def do_auth_character(self, character_id : str) -> FFXIVAuthorizationResult:
Expand All @@ -184,13 +192,37 @@ def do_auth_character(self, character_id : str) -> FFXIVAuthorizationResult:

return FFXIVAuthorizationResult.FINISHED

def __api_get_account_info(self, api_key):
resp = requests.get(self.API_DOMAIN + self.API_URL_CHARACTER, params={'data': 'AC,FR'})
def __api_get_account_info(self, character_id : str):
resp = requests.get(self.API_DOMAIN + self.API_URL_CHARACTER + character_id, params={'data': 'AC,FR'})
result = None

try:
result = json.loads(resp.text)
except Exception:
logging.error('ffxivapi/__api_get_account_info: %s' % resp.text)

return (resp.status_code, result)
return (resp.status_code, result)

# async def download_installer(self):
# installer_path = os.path.join(tempfile.mkdtemp(), "ffxivsetup.exe")

# async with aiofiles.open(installer_path, mode="wb") as installer_bin:
# await installer_bin.write(await self.get_installer())

# return installer_path

# async def get_installer(self) -> bytes:
# return await self.get_file(url=self.INSTALL_URL)

# async def get_file(self, *args) -> bytes:
# return await (
# await self._authenticated_request("GET", *args, allow_redirects=False)
# ).read()

# async def _authenticated_request(self, method, *args) -> ClientResponse:
# response = await super().request(method, *args)
# if response.status == HTTPStatus.FOUND:
# self._auth_lost_callback()
# raise AuthenticationRequired()

# return response
33 changes: 33 additions & 0 deletions ffxiv_localgame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import os
import sys
import logging
import subprocess
import xml.etree.ElementTree as ElementTree
import ffxiv_tools

from typing import List

class FFXIVLocalGame(object):
def __init__(self, game_dir, game_executable):
self._dir = game_dir.lower()
self._executable = game_executable.lower()

def exe_name(self) -> str:
return self._executable

def run_game(self) -> None:
subprocess.Popen([os.path.join(self._dir, self._executable)], creationflags=0x00000008, cwd = self._dir)

def delete_game(self) -> None:
subprocess.Popen(ffxiv_tools.get_uninstall_exe(), creationflags=0x00000008, cwd = self._dir, shell=True)

def get_game_instances() -> List[FFXIVLocalGame]:
result = list()
install_folder = ffxiv_tools.get_installation_folder()

if not os.path.exists(install_folder):
return result

result.append(FFXIVLocalGame(install_folder + "\\boot\\", "ffxivboot.exe"))

return result
56 changes: 56 additions & 0 deletions ffxiv_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import platform
import sys
import os

if sys.platform == 'win32':
import winreg

def set_arch_keys():
if platform.machine().endswith('64'):
arch_keys = {winreg.KEY_WOW64_32KEY, winreg.KEY_WOW64_64KEY}
else:
arch_keys = {0}

return arch_keys

def get_installation_folder():
arch_keys = set_arch_keys()

for arch_key in arch_keys:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", 0, winreg.KEY_READ | arch_key)

for i in range(0, winreg.QueryInfoKey(key)[0]):
skey_name = winreg.EnumKey(key, i)
skey = winreg.OpenKey(key, skey_name)

try:
if (winreg.QueryValueEx(skey, 'DisplayName')[0] == "FINAL FANTASY XIV - A Realm Reborn"):
install_location = winreg.QueryValueEx(skey, 'InstallLocation')[0] + "\\FINAL FANTASY XIV - A Realm Reborn"
skey.Close()

return install_location
except OSError:
pass
finally:
skey.Close()

def get_uninstall_exe():
arch_keys = set_arch_keys()

for arch_key in arch_keys:
key = winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", 0, winreg.KEY_READ | arch_key)

for i in range(0, winreg.QueryInfoKey(key)[0]):
skey_name = winreg.EnumKey(key, i)
skey = winreg.OpenKey(key, skey_name)

try:
if (winreg.QueryValueEx(skey, 'DisplayName')[0] == "FINAL FANTASY XIV - A Realm Reborn"):
uninstall_exe = winreg.QueryValueEx(skey, 'UninstallString')[0]
skey.Close()

return uninstall_exe
except OSError:
pass
finally:
skey.Close()
Loading

0 comments on commit 7784444

Please sign in to comment.