FriendsOfGalaxy / galaxy-integration-origin

origin integration for galaxy
77 stars 14 forks source link

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

Open JoTechOfficial opened 1 year ago

JoTechOfficial commented 1 year ago

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

Judassem commented 1 year ago

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

Tjoeser commented 1 year ago

same problem

josuelmm commented 1 year ago

Any solution?

Freebeard77 commented 1 year ago

I guess no one gives a shit.

Karmidzhanov commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

Nutzzz commented 1 year ago

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 commented 1 year ago

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

Judassem commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 : https://github.com/lutris/lutris/issues/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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

BellezaEmporium commented 1 year ago

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

BellezaEmporium commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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

BellezaEmporium commented 1 year ago

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 commented 1 year ago

@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 commented 1 year ago

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 commented 1 year ago

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.

Nutzzz commented 1 year ago

For the 2 EA games I had installed originally, Galaxy sees them as installed and now I can use Play to start them with no issues.

However, I'm given a login prompt every time Galaxy opens, and I get "Connection lost." I can try Connect again and it goes to Offline. If I Disconnect and re-Connect, I can login and it will stay connected until I close Galaxy, and then the behavior repeats on the next launch. Occasionally Galaxy and all the plugins go offline entirely; I hadn't seen this behavior until now, and it's not my Internet connection. I think it came back on it's own eventually one time, but if I close and re-open Galaxy during one of these I have to re-login to GOG.

I was able to install a few games using the arrow or the top button, however they didn't show as installed even after a Disconnect/Connect.

Still, good work so far!

BellezaEmporium commented 1 year ago

For the 2 EA games I had installed originally, Galaxy sees them as installed and now I can use Play to start them with no issues.

However, I'm given a login prompt every time Galaxy opens, and I get "Connection lost." I can try Connect again and it goes to Offline. If I Disconnect and re-Connect, I can login and it will stay connected until I close Galaxy, and then the behavior repeats on the next launch. Occasionally Galaxy and all the plugins go offline entirely; I hadn't seen this behavior until now, and it's not my Internet connection. I think it came back on it's own eventually one time, but if I close and re-open Galaxy during one of these I have to re-login to GOG.

I was able to install a few games using the arrow or the top button, however they didn't show as installed even after a Disconnect/Connect.

Still, good work so far!

I'm trying to force Galaxy to re-connect after the token has expired (when the EA client tells you you've logged in successfully, it gives you the access token, its type and the validity). I'm playing with the lats section of the plugin for now.

As for the "play" section. I need to check the "local_games.py" files, which is, for now, the only file I haven't touched. It still points to the Origin program data. I'll try to fix it sooner in the day :)

EDIT : The "play" is influenced by the fact that, like a few posts pointed out, the new manifests are encrypted... We basically need to decrypt the manifest to get the information. The whole "get_msft_file" logic is to be reviewed here.

BellezaEmporium commented 1 year ago

So far, i'm trying to decrypt the IS file from Python using this "tutorial" : https://github.com/erri120/GameFinder/wiki/EA-Desktop

Though I can't seem to decrypt mine. Some questions are popping off my head concerning WMIC :

Nutzzz commented 1 year ago

You want all of the results of each of the WMI calls (FYI, Win32_VideoController are video cards, not monitors, but you still might have multiple). I did my own implementation of the decryption in C#, so I'd be happy to help if you want to share what you have so far.

EDIT: Here's a minimal version of my C# EA decrypter.

BellezaEmporium commented 1 year ago

You want all of the results of each of the WMI calls (FYI, Win32_VideoController are video cards, not monitors, but you still might have multiple). I did my own implementation of the decryption in C#, so I'd be happy to help if you want to share what you have so far.

EDIT: Here's a minimal version of my C# EA decrypter.

Thank you for sharing your code. I'm trying to do the same with Python in order to reconstruct Galaxy's Local DB. I have used the WMI library in order to do this, however I can't decrypt it. I have multiple PCIs (AMD CPU & NVIDIA GPU), so I wonder if this was the culprit. Your code only seemed to have picked up the NVIDIA one.

EDIT : Compiled your test, works perfectly on my side. I did have the HW info pop up, which is excellent for me, for comparison purposes.

EDIT 2 : Thanks to your code, it permitted me to find out I had 2 issues in the Python WMIC result :

I've fixed them.

I've also permitted myself to add a few console logs in order to make sure I have the correct key when generating, which proved me wrong for my Python implementation... it's quite odd.

BellezaEmporium commented 1 year ago

Here's the final Python result on "how to decrypt IS" (thanks @Nutzzz for his decryptor)

def _decrypt_IS(filepath):
    if platform.system() == "Windows":
        # Retrieve system information
        sheesh = wmi.WMI()

        for baseboard in sheesh.Win32_BaseBoard():
            baseboard_manufacturer = baseboard.Manufacturer
            baseboard_serial_number = baseboard.SerialNumber
        for bios in sheesh.Win32_BIOS():
            bios_manufacturer = bios.Manufacturer
            bios_serial_number = bios.SerialNumber
        for disk in sheesh.Win32_LogicalDisk():
            if disk.Caption == "C:":
                volume_serial_number = disk.VolumeSerialNumber
        for video in sheesh.Win32_VideoController():
            # get the processor video controller, not the gpu one
            if video.VideoProcessor.startswith("Intel") or video.VideoProcessor.startswith("AMD"):
                video_controller_pnp_device_id = video.PNPDeviceID
        for processor in sheesh.Win32_Processor():
            processor_manufacturer = processor.Manufacturer
            processor_id = processor.ProcessorId
            processor_name = processor.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 + ';'
        # 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()

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

        # Open file and decrypt
        with open(filepath, 'rb') as f:
            encrypted_data = f.read()
            decrypted_data = bytearray()
            for i in range(len(encrypted_data)):
                decrypted_data.append(encrypted_data[i] ^ key_hash[i % len(key_hash)])

    elif platform.system() == "Darwin":
        # NOT IMPLEMENTED YET
        pass
    elif platform.system() == "Linux":
        # NOT IMPLEMENTED YET
        pass

As I do not have a hand on a Linux or Mac having EA Desktop installed, the logic might differ.

Note that you will need HMI and hashlib for this operation.

BellezaEmporium commented 1 year ago

Here's the final Python result on "how to decrypt IS" (thanks @Nutzzz for his decryptor)

snip

As I do not have a hand on a Linux or Mac having EA Desktop installed, the logic might differ.

Note that you will need HMI and hashlib for this operation.

I've made what seems to be the best copy/paste of the Windows variant to Mac and Linux, to be tested.

I'm trying to modify the WMI variant to a subprocess version (to prevent adding additional libraries) and i'm battling to make sure Python doesn't add trailing spaces or remove the necessary ones.

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.

Nutzzz commented 1 year ago

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
BellezaEmporium commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

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?

BellezaEmporium commented 1 year ago

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 commented 1 year ago
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 commented 1 year ago
ModuleNotFoundError: No module named 'pywintypes'

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

BellezaEmporium commented 1 year ago
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 commented 1 year ago

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 commented 1 year ago

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 commented 1 year ago

I've added my version of the code.

Nutzzz commented 1 year ago

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