Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Origin is discontinued, needs an update to support new Ea App #44

Open
KowabungaOfficial opened this issue Dec 21, 2022 · 67 comments
Open

Comments

@KowabungaOfficial
Copy link

https://www.ea.com/ea-app

@KowabungaOfficial KowabungaOfficial changed the title Origin is discontinued, needs an update to new Ea App Origin is discontinued, needs an update to support new Ea App Dec 21, 2022
@Judassem
Copy link

Judassem commented Aug 8, 2023

Same problem here. Can't login as Origin is no longer supported.

@Tjoeser
Copy link

Tjoeser commented Aug 15, 2023

same problem

@josuelmm
Copy link

Any solution?

@Freebeard77
Copy link

I guess no one gives a shit.

@Karmidzhanov
Copy link

I have the same issue, it won't accept my login credentials even though they're correct. Will this ever be fixed? EA/Origin is the only launcher that can't sync, so my library will never be complete..

@RoninX74
Copy link

I have the same issue, it won't accept my login credentials even though they're correct. Will this ever be fixed? EA/Origin is the only launcher that can't sync, so my library will never be complete..

Same problem I am having... There doesn't seem to be support for the extended security code authorization which the EA app uses.

@RoninX74
Copy link

I'm guessing whoever developed this extension is no longer supporting it. I just noticed the date this thread was started. it's been over 3/4 of a year and nothing. Sad!

@fireballhh
Copy link

If anyone knows how to use the new login url in plugin.py it might work. I dont have a clue how to do it probably.

https://signin.ea.com/p/juno/login?execution=e2004275468s1&initref=https%3A%2F%2Faccounts.ea.com%3A443%2Fconnect%2Fauth%3Finitref_replay%3Dfalse%26display%3DjunoWeb%252Flogin%26response_type%3Dcode%26redirect_uri%3Dhttps%253A%252F%252Fwww.ea.com%252Flogin_check%26locale%3Dde_DE%26client_id%3DEADOTCOM-WEB-SERVER

@Tjoeser
Copy link

Tjoeser commented Sep 17, 2023

I tried it but I can't make it work either

@Nutzzz
Copy link

Nutzzz commented Sep 17, 2023

I'd suggest either looking at how Playnite deals with the API, or use the method described here to pull the data out of the local database. Unfortunately, the latter method requires a bit of effort to decrypt it.

@BellezaEmporium
Copy link

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

@Judassem
Copy link

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

How do I try this out? What do I need to do?

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 18, 2023

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

How do I try this out? What do I need to do?

Simply copy and paste the code to Users\ your username \AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0

@Nutzzz
Copy link

Nutzzz commented Sep 18, 2023

Simply copy and paste the code to Users\ your username \AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0

To be clear, you click the green Code button, choose Download ZIP, extract the file, then copy the contents of the "src" folder over an existing install of the Origin plugin, and overwrite the duplicate files.

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

So, it's a start. I'm able to login again now, but after [trying to start a game] the EA app opens I eventually get "You don't have access - This title may have been downloaded to your device by another user who owns the game. Log in to a different account or restart the app to try again" on two games I tried. I'm able to open them just fine from within the app.

If I'm not mistaken, all you have to do is switch from using the OfferId (Origin.OFR.50.xxxxxxx) to the SoftwareId (Origin.SFT.50.yyyyyyy) instead. [Note: not the same number.] EDIT: I was mistaken.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 19, 2023

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

So, it's a start. I'm able to login again now, but after the EA app opens I eventually get "You don't have access - This title may have been downloaded to your device by another user who owns the game. Log in to a different account or restart the app to try again" on two games I tried. I'm able to open them just fine from within the app.

If I'm not mistaken, all you have to do is switch from using the OfferId (Origin.OFR.50.xxxxxxx) to the SoftwareId (Origin.SFT.50.yyyyyyy) instead. [Note: not the same number.]

I'll check the issue tomorrow. Will compare with the titles I have.

@Freebeard77
Copy link

Simply copy and paste the code to Users\ your username \AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0

To be clear, you click the green Code button, choose Download ZIP, extract the file, then copy the contents of the "src" folder over an existing install of the Origin plugin, and overwrite the duplicate files.

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

So, it's a start. I'm able to login again now, but after the EA app opens I eventually get "You don't have access - This title may have been downloaded to your device by another user who owns the game. Log in to a different account or restart the app to try again" on two games I tried. I'm able to open them just fine from within the app.

If I'm not mistaken, all you have to do is switch from using the OfferId (Origin.OFR.50.xxxxxxx) to the SoftwareId (Origin.SFT.50.yyyyyyy) instead. [Note: not the same number.]

Thank you, Nutzzz! I would not have figured that out on my own. I initially tried to copy the entire contents of the zip file into the directory. That obviously didn't work. :P

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 19, 2023

Simply copy and paste the code to Users\ your username \AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0

To be clear, you click the green Code button, choose Download ZIP, extract the file, then copy the contents of the "src" folder over an existing install of the Origin plugin, and overwrite the duplicate files.

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

So, it's a start. I'm able to login again now, but after [trying to start a game] the EA app opens I eventually get "You don't have access - This title may have been downloaded to your device by another user who owns the game. Log in to a different account or restart the app to try again" on two games I tried. I'm able to open them just fine from within the app.

If I'm not mistaken, all you have to do is switch from using the OfferId (Origin.OFR.50.xxxxxxx) to the SoftwareId (Origin.SFT.50.yyyyyyy) instead. [Note: not the same number.]

On first checkings, it is still Origin.OFR.50.xxxxxxx being used. However, all the links that were used on the Origin variant are different on EA Desktop.

ex : PID, Entitlements etc etc... are now on https://service-aggregation-layer.juno.ea.com/graphql

image

What's useful with GraphQL is that you can ask for any data you want, as long as your JSON query is correct and can be manipulated by the server to retrieve results.

Some others however are different :

image

The main issue seems to be on the "origin2://" hyperlinks. While it uses the same kind of ID, I don't think it works. Let me do further checks.

EDIT : lutris/lutris#4996 seems to point out the issue you're mentioning @Nutzzz. I'll have to ask directly through EA's new GraphQL in order to get the correct gameID for launching.

@Nutzzz
Copy link

Nutzzz commented Sep 19, 2023

Ah, sorry, my assumption was incorrect.

(The launcher I was working on uses the .exe files instead of the protocol to launch a game, but when moving from the Origin platform to EA, I had to rely on the Software ID rather than the Offer ID to get the file location; I guessed incorrectly that a similar change in the protocol launch method was necessary.)

@BellezaEmporium
Copy link

Ah, sorry, my assumption was incorrect.

(The launcher I was working on uses the .exe files instead of the protocol to launch a game, but when moving from the Origin platform to EA, I had to rely on the Software ID rather than the Offer ID to get the file location; I guessed incorrectly that a similar change in the protocol launch method was necessary.)

No worries.

It's a few more steps in order to make it work properly I suppose. One suggested using the EA API, though I only seem to find the correct IDs on the "_Installer" folders of already installed games. While it should normally be seen from the API, I can't seem to put my hand on it. The different GraphQL queries i've catched make no such mention.

@Nutzzz
Copy link

Nutzzz commented Sep 19, 2023

The link you provided seems to indicate that you should get a ContentID in the .json response.

@BellezaEmporium
Copy link

Maybe if I manually ask for it. Let me give it a try.

@BellezaEmporium
Copy link

I only get Origin.OFR results. However, DLCs and other results seem to work. Had the "Battlefront upgrade DLC" pop up when I tried launching stuff.

@Nutzzz
Copy link

Nutzzz commented Sep 19, 2023

Hm, I don't really have any experience with the API, but FWIW it looks like you can take the OFR and make a request to https://api1.origin.com/ecommerce2/public/Origin.OFR.50.xxxxxxx/en_US and from there you can pull out "publishing" > "publishingAttributes" > "contentId"

@BellezaEmporium
Copy link

Hm, I don't really have any experience with the API, but FWIW it looks like you can take the OFR and make a request to https://api1.origin.com/ecommerce2/public/Origin.OFR.50.xxxxxxx/en_US and from there you can pull out "publishing" > "publishingAttributes" > "contentId"

Either financeId or projectNumber seems to be quite accurate.

or, indeed,

contentId | "194908"

@BellezaEmporium
Copy link

In the case of https://api1.origin.com/ecommerce2/consolidatedentitlements/ <- projectId seems to be quite close.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 19, 2023

OK, i've found it. I think i've corrected the main issue. Now the main goal is to install the game when wanted, and launch it when it's available. Some are quite weird in the way it works (Battlefield games got like tons of IDs).

EDIT : OK, problem is solved :)

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 19, 2023

@Nutzzz please try latest commit and tell me if it works.

I've ditched the OfferID to ContentID. Tests were successful on my side. If it works for you then I suppose the plugin is fully fixed :)

@Tjoeser
Copy link

Tjoeser commented Sep 19, 2023

I had the same as Nutzzz with the error saying that its not installed on my account.
Now that I tried your latest commit it seems to work but all the games from ea have the "Install" button.

So the installed ones and the noninstalled ones.

But the ones that I have installed just start when I press the button.
The ones that aren't installed give me an error in the ea app saying "Game not installed".

Do you know what's going on?

@BellezaEmporium
Copy link

I must say I stumbled upon this issue too... though I do not know how that's triggered out in Galaxy. I'll need to do further checking, as it might be manifest-related or Windows stuff.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 25, 2023

EDIT2 : I guess I'll have no choice but to ship this decryption system separately from Galaxy. Sounds like there's a bit of an issue there. You'll have to decrypt your IS file, and then the rest will be done.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 25, 2023

Here's the decryption program. It will create the necessary decrypted IS JSON file. In order for Galaxy to work with it afterwards, you'll need to add the decrypted JSON file in the EA Desktop folder. (in case you're always opening Python as admin, you may modify this code in order to do so).

There's one thing I haven't found out is the PNP device for Mac users.

import subprocess
import platform
import os
if platform.system() == "Darwin":
    import psutil
from hashlib import sha1, sha3_256
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

# changes in overall functioning (as of its release) constraints us to use a decryption method in order to check which game's installed
# inputs the filepath of the manifest file
# outputs the decrypted JSON file
filepath = input("Enter the filepath of the manifest file: ")
iv = "allUsersGenericIdIS".encode('ascii')
iv_hash = sha3_256(iv).digest()

if platform.system() == "Windows":
    query_baseboard = subprocess.check_output('wmic baseboard get Manufacturer,SerialNumber /format:list', shell=True)
    if query_baseboard:
        data = {}
        for line in query_baseboard.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        baseboard_manufacturer = data.get('Manufacturer')
        baseboard_serial_number = data.get('SerialNumber')
    query_bios = subprocess.check_output('wmic bios get Manufacturer,SerialNumber /format:list')
    if query_bios:
        data = {}
        for line in query_bios.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        bios_manufacturer, bios_serial_number = data.get('Manufacturer'), data.get('SerialNumber')
    query_logicaldisk = subprocess.check_output('wmic logicaldisk where "Caption=\'C:\'" get VolumeSerialNumber /format:list')
    if query_logicaldisk:
        data = {}
        for line in query_logicaldisk.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        volume_serial_number = data.get('VolumeSerialNumber')
    query_videocontroller = subprocess.check_output('wmic path win32_videocontroller where "VideoProcessor like \'Intel%\' or VideoProcessor like \'AMD%\'" get PNPDeviceID /format:list')
    if query_videocontroller:
        data = {}
        for line in query_videocontroller.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        video_controller_pnp_device_id = data.get('PNPDeviceID').replace("&amp;", "&")
    query_cpu = subprocess.check_output('wmic cpu get Manufacturer,ProcessorId /format:list')
    if query_cpu:
        data = {}
        for line in query_cpu.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        processor_manufacturer, processor_id = data.get('Manufacturer'), data.get('ProcessorId')
    query_cpu_name = subprocess.check_output('wmic cpu get Name /format:list')
    if query_cpu_name:
        data = {}
        for line in query_cpu_name.decode('utf-8').split('\n'):
            if '=' in line:
                key, value = line.split('=', 1)
                data[key] = value.rstrip('\r')
        processor_name = data.get('Name')
    
    # sha1 string
    hw_info = baseboard_manufacturer + ";" + baseboard_serial_number + ";" + bios_manufacturer + ";" + bios_serial_number + ";" + volume_serial_number + ";" + video_controller_pnp_device_id + ";" + processor_manufacturer + ";" + processor_id + ";" + processor_name + ";"
    print("Got hardware info: %s", hw_info)

    # Calculate SHA1 Hash of hardware string
    hw_info_bytes = hw_info.encode('ascii')
    hw_hash = sha1(hw_info_bytes).digest()

    print("Got hardware hash: %s", hw_hash.hex())

    hash_str = 'allUsersGenericIdIS' + hw_hash.hex().lower()

    # Calculate SHA3 256 Hash of full string
    hash_bytes = hash_str.encode('ascii')
    key_hash = sha3_256(hash_bytes).digest()

    print("Got key hash: %s", key_hash.hex())

    cipher = Cipher(algorithms.AES(key_hash), modes.CBC(iv_hash[0:16]))

    # Create a decryptor object
    decryptor = cipher.decryptor()

    # Open input and output files
    with open(filepath, 'rb') as infile, open("IS.json", 'wb') as outfile:
        # Read the first 64 bytes and discard them
        infile.read(64)
        block_size = 16

        while True:
            # Read a block from the input file
            block = infile.read(block_size)
            if not block:
                break  # Reached end of file

            # Decrypt the block and write to the output file
            decrypted_block = decryptor.update(block)
            outfile.write(decrypted_block)

        print("IS decrypted successfully. Move the file to the EA Desktop folder. Enjoy using Galaxy!")
    
elif platform.system() == "Darwin":
    # Retrieve system information
    baseboard_manufacturer = ''
    baseboard_serial_number = ''
    bios_manufacturer = ''
    bios_serial_number = ''
    volume_serial_number = ''
    processor_manufacturer = ''
    processor_id = ''
    processor_name = ''

    # Baseboard information
    baseboard_manufacturer = subprocess.check_output(["system_profiler", "SPHardwareDataType"])
    baseboard_manufacturer = baseboard_manufacturer.decode('utf-8').split(':')[-1].strip()

    # BIOS information (Mac doesn't have BIOS in the traditional sense)
    bios_manufacturer = "Apple"
    bios_serial_number = subprocess.check_output(["system_profiler", "SPHardwareDataType"])
    bios_serial_number = bios_serial_number.decode('utf-8').split(':')[-1].strip()

    # Disk information
    volume_serial_number = subprocess.check_output(["system_profiler", "SPStorageDataType"])
    volume_serial_number = volume_serial_number.decode('utf-8').split('Serial Number (system):')[-1].strip()

    # Video controller information
    # to be determined

    # Processor information
    processor_info = subprocess.check_output(["sysctl", "-n", "machdep.cpu.brand_string"])
    processor_info = processor_info.decode('utf-8').strip()

    processor_manufacturer = "Intel"  # Assuming Mac uses Intel processors
    processor_name = processor_info


    # sha1 string
    hw_info = baseboard_manufacturer + ";" + baseboard_serial_number + ";" + bios_manufacturer + ";" + bios_serial_number + ";" + volume_serial_number + ";" + video_controller_pnp_device_id + ";" + processor_manufacturer + ";" + processor_id + ";" + processor_name + ';'
    # Calculate SHA1 Hash of hardware string
    hw_info_bytes = hw_info.encode('ascii')
    hw_hash = sha1(hw_info_bytes).digest()
    hash_str = 'allUsersGenericIdIS' + hw_hash.hex().lower()

    print("Got hardware info: %s", hw_info)

    # Calculate SHA3 256 Hash of full string
    hash_bytes = hash_str.encode('ascii')
    key_hash = sha3_256(hash_bytes).digest()

    print("Got key hash: %s", key_hash.hex())

    cipher = Cipher(algorithms.AES(key_hash), modes.CBC(iv_hash[0:16]))

    # Create a decryptor object
    decryptor = cipher.decryptor()

    # Open input and output files
    with open(filepath, 'rb') as infile, open("IS.json", 'wb') as outfile:
        # Read the first 64 bytes and discard them
        infile.read(64)
        block_size = 16

        while True:
            # Read a block from the input file
            block = infile.read(block_size)
            if not block:
                break  # Reached end of file

            # Decrypt the block and write to the output file
            decrypted_block = decryptor.update(block)
            outfile.write(decrypted_block)

        print("IS decrypted successfully. Move the file to the EA Desktop folder. Enjoy using Galaxy!")
    
elif platform.system() == "Linux":
    # Retrieve system information
    baseboard_manufacturer = ''
    baseboard_serial_number = ''
    bios_manufacturer = ''
    bios_serial_number = ''
    volume_serial_number = ''
    video_controller_pnp_device_id = ''
    processor_manufacturer = ''
    processor_id = ''
    processor_name = ''

    # Baseboard information
    with open('/sys/class/dmi/id/board_vendor', 'r') as f:
        baseboard_manufacturer = f.read().strip()

    with open('/sys/class/dmi/id/board_serial', 'r') as f:
        baseboard_serial_number = f.read().strip()

    # BIOS information
    with open('/sys/class/dmi/id/bios_vendor', 'r') as f:
        bios_manufacturer = f.read().strip()

    with open('/sys/class/dmi/id/bios_version', 'r') as f:
        bios_serial_number = f.read().strip()

    # Disk information (assuming the root partition is mounted at /)
    partition = psutil.disk_partitions(all=False)[0]
    volume_serial_number = psutil.disk_usage(partition.mountpoint).serial

    # Video controller information (you may need to adjust this)
    for device in psutil.pids():
        try:
            cmdline = psutil.Process(device).cmdline()
            if "Xorg" in cmdline or "xorg" in cmdline:
                # Extract video controller information from Xorg process
                # TEST THIS
                video_controller_pnp_device_id = cmdline[cmdline.index("Xorg") + 1]
                pass
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
            pass

    # Processor information
    processor_info = os.popen("lscpu").read()
    lines = processor_info.split('\n')
    for line in lines:
        if "Vendor ID:" in line:
            processor_manufacturer = line.split(':')[1].strip()
        elif "Model name:" in line:
            processor_name = line.split(':')[1].strip()

    # sha1 string
    hw_info = baseboard_manufacturer + ";" + baseboard_serial_number + ";" + bios_manufacturer + ";" + bios_serial_number + ";" + volume_serial_number + ";" + video_controller_pnp_device_id + ";" + processor_manufacturer + ";" + processor_id + ";" + processor_name + ';'
    # Calculate SHA1 Hash of hardware string
    hw_info_bytes = hw_info.encode('ascii')
    hw_hash = sha1(hw_info_bytes).digest()
    hash_str = 'allUsersGenericIdIS' + hw_hash.hex().lower()

    print("Got hardware info: %s", hw_info)

    # Calculate SHA3 256 Hash of full string
    hash_bytes = hash_str.encode('ascii')
    key_hash = sha3_256(hash_bytes).digest()

    print("Got key hash: %s", key_hash.hex())

    cipher = Cipher(algorithms.AES(key_hash), modes.CBC(iv_hash[0:16]))

    # Create a decryptor object
    decryptor = cipher.decryptor()

    # Open input and output files
    with open(filepath, 'rb') as infile, open("IS.json", 'wb') as outfile:
        # Read the first 64 bytes and discard them
        infile.read(64)
        block_size = 16

        while True:
            # Read a block from the input file
            block = infile.read(block_size)
            if not block:
                break  # Reached end of file

            # Decrypt the block and write to the output file
            decrypted_block = decryptor.update(block)
            outfile.write(decrypted_block)

        print("IS decrypted successfully. Move the file to the EA Desktop folder. Enjoy using Galaxy!")
else:
    # Unsupported platform
    pass

@BellezaEmporium
Copy link

Now, about the .eacrc. It seems the encoding is different from the original Origin map.crc files, so i'll need to check how can I do it.

@Nutzzz
Copy link

Nutzzz commented Sep 28, 2023

Now, about the .eacrc. It seems the encoding is different from the original Origin map.crc files, so i'll need to check how can I do it.

Hm, I feel like getting the install size is fairly low priority.

@Nutzzz
Copy link

Nutzzz commented Sep 29, 2023

EDIT : In order to make everything work on the Galaxy plugin, I need to add the cryptography library. The main issue is... I can't seem to find the virtualenv in which Galaxy loads its plugins from. Meaning I can't install any plugins.

I was looking at this just now. Look at how the Blizzard plugin does it (runs pip based on a dependencies file):

tasks.py
requirements/app.txt
requirements/dev.txt

Hi @BellezaEmporium
Reading the README here https://github.com/GOG-Nebula/galaxy-integration-steam helped me figure out how to compile an integration properly. Admittedly, my version with cryptography and WMI added to app.txt and calling a function based on your prior "how to decrypt" code snippet is currently crashing, but those libraries appear to have been added in successfully. EDIT: See my fork if you like.

Could you post your latest?

Nutzzz added a commit to Nutzzz/galaxy-integration-ead that referenced this issue Sep 29, 2023
@BellezaEmporium
Copy link

EDIT : In order to make everything work on the Galaxy plugin, I need to add the cryptography library. The main issue is... I can't seem to find the virtualenv in which Galaxy loads its plugins from. Meaning I can't install any plugins.

I was looking at this just now. Look at how the Blizzard plugin does it (runs pip based on a dependencies file):

tasks.py
requirements/app.txt
requirements/dev.txt

Hi @BellezaEmporium
Reading the README here https://github.com/GOG-Nebula/galaxy-integration-steam helped me figure out how to compile an integration properly. Admittedly, my version with cryptography and WMI added to app.txt and calling a function based on your prior "how to decrypt" code snippet is currently crashing, but those libraries appear to have been added in successfully. EDIT: See my fork if you like.

Could you post your latest?

Nice to see you've managed your path through. I'll try, and post my version. Been working on the .eacrc file support.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 29, 2023

2023-09-29 19:30:28,609 - root - ERROR - Plugin exception
Traceback (most recent call last):
  File "C:\Program Files (x86)\GOG Galaxy\plugin_runner.py", line 35, in <module>
    module = importlib.import_module(filename)
  File "D:\obj\Windows-Release\37win32_Release\msi_python\zip_win32\__init__.py", line 127, in import_module
  File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
  File "<frozen importlib._bootstrap>", line 983, in _find_and_load
  File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 728, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "C:\Users\-----\AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0\plugin.py", line 25, in <module>
    from local_games import get_local_content_path, LocalGames, parse_map_crc_for_total_size
  File "C:\Users\-----\AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0\local_games.py", line 11, in <module>
    from wmi import WMI
  File "C:\Users\-----\AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0\wmi.py", line 105, in <module>
    from win32com.client import GetObject, Dispatch
  File "C:\Users\-----\AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0\win32com\__init__.py", line 8, in <module>
    import pythoncom
  File "C:\Users\-----\AppData\Local\GOG.com\Galaxy\plugins\installed\origin_7f53219b-4e2b-4591-9f4f-dfc5f4ba9eb0\pythoncom.py", line 2, in <module>
    import pywintypes
ModuleNotFoundError: No module named 'pywintypes'

This is going to be a long ride...

@Nutzzz
Copy link

Nutzzz commented Sep 29, 2023

ModuleNotFoundError: No module named 'pywintypes'

I think this one is solved by doing "pip install pypiwin32" in your global (not venv) environment.

@BellezaEmporium
Copy link

BellezaEmporium commented Sep 29, 2023

ModuleNotFoundError: No module named 'pywintypes'

I think this one is solved by doing "pip install pypiwin32" in your global (not venv) environment.

I already do have this package up in my programs. It's because it strictly searchs for a package named "pywintypes". It exists in the win32/lib folder, but then goes by a series of other errors...

@Nutzzz
Copy link

Nutzzz commented Sep 29, 2023

Hm, I got past that one somehow without a pywintypes package specifically. Is pywin32 in your venv "pip list"?

EDIT: No, I lied. I am stuck on that one still...
EDIT 2: It's only my "invoke test" that has been slowly getting better, except I just realized I'm running it from Admin, which I guess is why I was getting anywhere with it.

@BellezaEmporium
Copy link

Hm, I got past that one somehow without a pywintypes package specifically. Is pywin32 in your venv "pip list"?

EDIT: No, I lied. I am stuck on that one still... EDIT 2: It's only my "invoke test" that has been slowly getting better, except I just realized I'm running it from Admin, which I guess is why I was getting anywhere with it.

Yes, I've even did the pywin32_postinstall.py file (as Admin)

@BellezaEmporium
Copy link

I've added my version of the code.

@Nutzzz
Copy link

Nutzzz commented Oct 6, 2023

@BellezaEmporium : I had a little time to do some experiments. I was able to get pywintypes module to import, but it seems like we can't get at the win32api .dlls from the GOG context, so we're stuck with calling wmic.

I fixed an issue with NVIDIA adapters in a PR to your dev branch. I also submitted a draft PR that creates the decrypted IS file by calling python as admin. As explained in the PR, this will require some thought to do a proper implementation, but it's a start.

@Tjoeser
Copy link

Tjoeser commented Oct 14, 2023

hey guys could you upload the latest version you have thats stable to the repo?

@Nutzzz
Copy link

Nutzzz commented Oct 15, 2023

hey guys could you upload the latest version you have thats stable to the repo?

I wouldn't say any of it is quite ready for primetime, stick with PR #47 here or with the "main" branch of @BellezaEmporium 's fork (if I'm not mistaken, they're pretty much the same, after he branched "dev" and rolled back).

We've made some progress in dev, and we've been discussing the progress there in one of my out-of-date PRs. However, it's not really ready for testing yet.

@BellezaEmporium
Copy link

hey guys could you upload the latest version you have thats stable to the repo?

I wouldn't say any of it is quite ready for primetime, stick with PR #47 here or with the "main" branch of @BellezaEmporium 's fork (if I'm not mistaken, they're pretty much the same, after he branched "dev" and rolled back).

We've made some progress in dev, and we've been discussing the progress there in one of my out-of-date PRs. However, it's not really ready for testing yet.

What he just said. We could pull out the dev branch onto main but there's some functionalities that are still WIP that we need to work on.

@ABaumher
Copy link

ABaumher commented Nov 4, 2023

@BellezaEmporium @Nutzzz I'm the guy who worked on fixing the Steam Plugin and also a member of GOG Nebula. I can't really guarentee a lot of time to help with your plugin attempts, but I'd like to reach out anyway and see what your progress is and if you're stuck on anything i can help with. I saw a fork mentioned on the steam page but it looks like you've already incorporated the same changes in yours, so that's not particularly helpful :(

That said, i'm intimately (albeit reluctantly) familiar with how Galaxy Plugins work, so if you want to pick my brain, let me know. I can do some testing when needed as well. One of the devs at Nebula runs Mac, so if you need builds for MacOS, i can see if that is an option moving forward as well.

@Nutzzz
Copy link

Nutzzz commented Nov 8, 2023

@ABaumher : I'm not very familiar with Python (not that it's particularly hard), let alone Galaxy plugins, but I've had some experience with analyzing the various game launcher databases/manifests, including EA's, so I was able to help out with a few tasks. After we got the IS file decryption working I spent a lot of time figuring out a way to get admin rights to a guaranteed Python environment--the one that comes with Galaxy--but now that I did so, I'm unclear why that was necessary since I can read the encrypted IS file just fine in a C# program [without admin rights]. In any case, now that we have a decrypted .json file, I figured the parsing would be fairly easy to implement based on the prior art, but @BellezaEmporium hasn't made any public check-ins, and I haven't investigated further.

@BellezaEmporium
Copy link

@ABaumher : I'm not very familiar with Python (not that it's particularly hard), let alone Galaxy plugins, but I've had some experience with analyzing the various game launcher databases/manifests, including EA's, so I was able to help out with a few tasks. After we got the IS file decryption working I spent a lot of time figuring out a way to get admin rights to a guaranteed Python environment--the one that comes with Galaxy--but now that I did so, I'm unclear why that was necessary since I can read the encrypted IS file just fine in a C# program. In any case, now that we have a decrypted .json file, I figured the parsing would be fairly easy to implement based on the prior art, but @BellezaEmporium hasn't made any public check-ins, and I haven't investigated further.

I had little time to check again, been busy on personal stuff.

What we need basically now is :

  • Find the games' size (Galaxy seems to want it ?). I've found a few .eacrc files but no way to read them properly.

  • As @Nutzzz said on the opened PR in @BellezaEmporium/galaxy-integration-ead, I need to clean the junk data (placed at the end of the decrypted JSON file) more efficiently, and this would need some Python tricks (maybe there's something to do with rsplit and removing the other side)

  • I'm working on making sure everything works perfectly. Though we need elevated privileges to read and decrypt the IS file, it's then way easier to check in the JSON. But as noted, we'll need to verify if the IS file changes or not. To do so, I've thought about logging the IS file size in memory or in a database/file. If the IS file changes, we re-decrypt the file and overwrite the JSON. Though I have concerns over persons doing multi-accounts with EA Desktop. Maybe add a threshold (if the gap is too important between the two file sizes, actually ask user if it's on purpose or not)

@ABaumher
Copy link

Fyi, file size is optional. Afaik the documentation says you can just use 0 if you don't have it. It's nice to have for things like install size but if it's not easily available don't bother. You actually can enable or disable features in the plugin simply by adding or removing the related functions to the plugin.py file, so if things like achievements or chat won't work you can pull those out.

Can you give me a bit of context on the .IS file? I'm realizing this is not something i can just look at on my phone and understand; I'll need to look at it on my gaming pc when i have time. From what I've read so far the old plugin code would parse manifest files, but an earlier comment said they're now encrypted.

@BellezaEmporium
Copy link

BellezaEmporium commented Nov 10, 2023

The IS file is an encrypted JSON file made by EA in order to retrace the owned games inside your computer. When you install or own a new game, the IS file is rewritten, adding tons of data related to it, such as the SoftwareID. the base slug, some installation checks and so on. Everything is written there, from base games to downloadable content.

image

This excellent wiki article from erri120 resumes how the encryption works and, by working with @Nutzzz we've managed to port the decryption system to Python.

@ABaumher
Copy link

The plugin architecture has a tick function that runs periodically. You could try decrypting the is file every minute or so and see what's changed. Or like you said, you can cache the file size and compare that on that tick. Gog has a built-in cache for us to use with each plugin (it's actually a database file in program data) that you could use for this purpose. The name escapes me at the moment but the plugin class has a dictionary you can write to that is then stored in that cache. Steam plugin caches almost everything because parsing hundreds of games that haven't changed is usually not necessary. you could probably cache the installed games info if the plugin doesn't already and just send an update instead of reinitializing everything when the user adds or removes one.

An aside, I'm not sure the folks at nebula would be overly happy requiring admin, but I'd prioritize getting it working before worrying about that. I'm sure people would much prefer the plugin works and be willing to hit "allow this program"

@BellezaEmporium
Copy link

BellezaEmporium commented Nov 10, 2023

The plugin architecture has a tick function that runs periodically. You could try decrypting the is file every minute or so and see what's changed. Or like you said, you can cache the file size and compare that on that tick. Gog has a built-in cache for us to use with each plugin (it's actually a database file in program data) that you could use for this purpose. The name escapes me at the moment but the plugin class has a dictionary you can write to that is then stored in that cache. Steam plugin caches almost everything because parsing hundreds of games that haven't changed is usually not necessary. you could probably cache the installed games info if the plugin doesn't already and just send an update instead of reinitializing everything when the user adds or removes one.

An aside, I'm not sure the folks at nebula would be overly happy requiring admin, but I'd prioritize getting it working before worrying about that. I'm sure people would much prefer the plugin works and be willing to hit "allow this program"

The UAC is because the IS file is inside a protected folder inside EA Desktop's files. Copying or reading it is protected by user control. Might be different in another OS, but that's what I'm confronted with Windows.

Nebula's Steam plugin was a great aid in multiple use-cases. I'll continue checking the plugin in case certain ideas you've had can be applied inside the EAD plugin.

@ABaumher
Copy link

The UAC is because the IS file is inside a protected folder inside EA Desktop's files. Copying or reading it is protected by user control

I was afraid of that. Usually they're write protected but you can read them just fine, then either dump the contents to a temp file or just do it in memory. I'm curious since there's a way to do it without uac in c#, if there's a way to do it in python a well. Might be a difference in how python opens the file; maybe one of the 6 million python packages has a workaround, but again, not worth looking into until everything else is working

@ABaumher
Copy link

Not sure if you are using mypy or another linter but i generated type hints for the galaxy api at https://github.com/ABaumher/Galaxy-Integration-API-Python-Typing
Not sure how much you need to tweak with the actual plugin infrastructure code but if you do have to tweak things it can be helpful. There are actually some incorrectly documented types in gog's documentation that are corrected here, but they're usually insignificant

@FaithTheSlayer
Copy link

Here's my attempt at it : https://github.com/BellezaEmporium/galaxy-integration-ead

Thanks! It worked!

@BellezaEmporium
Copy link

A few updates here :

-> I've implemented IS file caching (onto temp folder) and reading it is now OK. Plugin works perfectly as-is right now on dev branch, but I want to be extra sure before shipping everything into the master branch.
-> I've found out EA's token lasts for 4 hours (JWT token tells everything), so the refresh system will be re-enhanced in order to keep the connection.
-> I'm currently tackling a few aspects of Origin vs. EA Desktop APIs onto a new project, which will be "release 0.42" of the integration.

@BellezaEmporium
Copy link

Once i'm done with the few final aspects of the project, i'm strongly considering to create an "one-click" installation system like certain plugins already do, and join forces with the GOG-Nebula community.

(with their authorization, I accept that my project becomes a GOG-Nebula official integration)

@BellezaEmporium
Copy link

BellezaEmporium commented Apr 17, 2024

After reconsideration I might need to fully rewrite the integration to support the new Juno API, as EA announced their EA Desktop app as a replacement for Origin in macOS, with the intent to migrate all users into EA Desktop soon. I believe the Origin API as we know it will disappear in favour of the new Juno API. Everything will be in GraphQL, which simplifies things a bit, but i'm afraid that the SHA256 used in the end of each request (and mandatory) changes over time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

13 participants