├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
└── workflows
│ └── nightly.yml
├── .gitignore
├── LICENSE
├── README.md
├── assets
├── custom
│ └── .gitkeep
├── fonts
│ ├── Formula1-Bold.ttf
│ ├── Formula1-Regular.ttf
│ └── NewNumbers.otf
└── images
│ ├── AMO.svg
│ ├── Antonelli4.png
│ ├── Hamilton4.png
│ ├── KA-min.png
│ ├── LH-min.png
│ ├── Logo.png
│ ├── MV-min.png
│ ├── Verstappen4.png
│ ├── alfaromeo.png
│ ├── alphatauri.png
│ ├── alphatauri.svg
│ ├── alphatauri2.png
│ ├── alpine.png
│ ├── alpine.svg
│ ├── alpine2.png
│ ├── andretti.png
│ ├── andretti.svg
│ ├── andretti2.png
│ ├── ant-lines.svg
│ ├── astonMartin.png
│ ├── audi.png
│ ├── australia.png
│ ├── austria.png
│ ├── azerbaiyan.png
│ ├── background.svg
│ ├── background2.svg
│ ├── background2light.svg
│ ├── background2og.svg
│ ├── background3.svg
│ ├── bahrain.png
│ ├── balgium.png
│ ├── brawn.png
│ ├── brawn.svg
│ ├── brawn2.png
│ ├── brazil.png
│ ├── canada.png
│ ├── car.png
│ ├── car.svg
│ ├── china.png
│ ├── engine.png
│ ├── ferrari.png
│ ├── gbr.png
│ ├── haas.png
│ ├── ham-lines.svg
│ ├── headAero.png
│ ├── hugoboss.png
│ ├── hugoboss.svg
│ ├── hugoboss2.png
│ ├── hungry.png
│ ├── icon.png
│ ├── icon2.png
│ ├── italy.png
│ ├── japan.png
│ ├── kick.png
│ ├── kick2.png
│ ├── kick3.png
│ ├── logo2.png
│ ├── logoAlter.png
│ ├── logoAlter.svg
│ ├── logoMod.svg
│ ├── logoMod2Colors.svg
│ ├── logoVector.png
│ ├── logoVector.svg
│ ├── logoVector3d.png
│ ├── lotus.png
│ ├── lotus.svg
│ ├── lotus2.png
│ ├── mclaren.png
│ ├── mclaren2.png
│ ├── mercedes.png
│ ├── mexico.png
│ ├── modback.png
│ ├── monaco.png
│ ├── ned.png
│ ├── news
│ ├── 102_pad.webp
│ ├── 105_pad.webp
│ ├── 107_pad.webp
│ ├── 10_pad.webp
│ ├── 116_pad.webp
│ ├── 11_pad.webp
│ ├── 12_pad.webp
│ ├── 13_pad.webp
│ ├── 142_pad.webp
│ ├── 144_pad.webp
│ ├── 14_pad.webp
│ ├── 15_pad.webp
│ ├── 17_pad.webp
│ ├── 18_pad.webp
│ ├── 1_pad.webp
│ ├── 1_shot.webp
│ ├── 23_pad.webp
│ ├── 242_pad.webp
│ ├── 245_pad.webp
│ ├── 255_pad.webp
│ ├── 279_pad.webp
│ ├── 286_pad.webp
│ ├── 2_pad.webp
│ ├── 2_shot.webp
│ ├── 373_pad.webp
│ ├── 376_pad.webp
│ ├── 3_pad.webp
│ ├── 3_shot.webp
│ ├── 4_shot.webp
│ ├── 5_shot.webp
│ ├── 6_pad.webp
│ ├── 6_shot.webp
│ ├── 77_pad.webp
│ ├── 7_shot.webp
│ ├── 81_pad.webp
│ ├── 83_pad.webp
│ ├── 87_pad.webp
│ ├── 8_shot.webp
│ ├── 95_pad.webp
│ ├── af1.webp
│ ├── af2.webp
│ ├── af_factory.webp
│ ├── al1.webp
│ ├── al2.webp
│ ├── al_factory.webp
│ ├── as1.webp
│ ├── as2.webp
│ ├── as_factory.webp
│ ├── at1.webp
│ ├── at2.webp
│ ├── at_factory.webp
│ ├── aus.webp
│ ├── aus_car.webp
│ ├── aus_tra.webp
│ ├── aut.webp
│ ├── aut_car.webp
│ ├── aut_tra.webp
│ ├── aze.webp
│ ├── aze_car.webp
│ ├── aze_tra.webp
│ ├── bah.webp
│ ├── bah_car.webp
│ ├── bah_tra.webp
│ ├── bel.webp
│ ├── bel_car.webp
│ ├── bel_tra.webp
│ ├── bra.webp
│ ├── bra_car.webp
│ ├── bra_tra.webp
│ ├── can.webp
│ ├── can_car.webp
│ ├── can_tra.webp
│ ├── champ1.webp
│ ├── champ2.webp
│ ├── champ3.webp
│ ├── champ4.webp
│ ├── champ5.webp
│ ├── chi.webp
│ ├── chi_car.webp
│ ├── chi_tra.webp
│ ├── con1.webp
│ ├── con2.webp
│ ├── con3.webp
│ ├── con4.webp
│ ├── con5.webp
│ ├── con6.webp
│ ├── con7.webp
│ ├── con8.webp
│ ├── con9.webp
│ ├── ct_factory.webp
│ ├── fe1.webp
│ ├── fe2.webp
│ ├── fe3.webp
│ ├── fe4.webp
│ ├── fe5.webp
│ ├── fe_factory.webp
│ ├── gbr.webp
│ ├── gbr_car.webp
│ ├── gbr_tra.webp
│ ├── ha1.webp
│ ├── ha2.webp
│ ├── ha_factory.webp
│ ├── hun.webp
│ ├── hun_car.webp
│ ├── hun_tra.webp
│ ├── imo.webp
│ ├── imo_car.webp
│ ├── imo_tra.webp
│ ├── ita.webp
│ ├── ita_car.webp
│ ├── ita_tra.webp
│ ├── jap.webp
│ ├── jap_car.webp
│ ├── jap_tra.webp
│ ├── mc1.webp
│ ├── mc2.webp
│ ├── mc_factory.webp
│ ├── me1.webp
│ ├── me2.webp
│ ├── me3.webp
│ ├── me4.webp
│ ├── me_factory.webp
│ ├── mex.webp
│ ├── mex_car.webp
│ ├── mex_tra.webp
│ ├── mia.webp
│ ├── mia_car.webp
│ ├── mia_tra.webp
│ ├── mon.webp
│ ├── mon_car.webp
│ ├── mon_tra.webp
│ ├── ned.webp
│ ├── ned_car.webp
│ ├── ned_tra.webp
│ ├── qat.webp
│ ├── qat_car.webp
│ ├── qat_tra.webp
│ ├── rb1.webp
│ ├── rb2.webp
│ ├── rb3.webp
│ ├── rb4.webp
│ ├── rb_factory.webp
│ ├── sau.webp
│ ├── sau_car.webp
│ ├── sau_tra.webp
│ ├── sgp.webp
│ ├── sgp_car.webp
│ ├── sgp_tra.webp
│ ├── spa.webp
│ ├── spa_car.webp
│ ├── spa_tra.webp
│ ├── uae.webp
│ ├── uae_car.webp
│ ├── uae_tra.webp
│ ├── usa.webp
│ ├── usa_car.webp
│ ├── usa_tra.webp
│ ├── veg.webp
│ ├── veg_car.webp
│ ├── veg_tra.webp
│ ├── wi1.webp
│ ├── wi2.webp
│ ├── wi3.webp
│ └── wi_factory.webp
│ ├── patreonLogo.svg
│ ├── placeholder.png
│ ├── porsche.png
│ ├── qatar.png
│ ├── raceEngineer2.png
│ ├── redbull.png
│ ├── renault.png
│ ├── renault.svg
│ ├── renault2.png
│ ├── sauber.png
│ ├── sauber2.png
│ ├── saudi.jpg
│ ├── singapore.png
│ ├── spain.png
│ ├── splash-logo.png
│ ├── sportingDirector.png
│ ├── technicalChief.png
│ ├── thumb.svg
│ ├── toyota.png
│ ├── toyota2.png
│ ├── uae.png
│ ├── usa.png
│ ├── ver-lines.svg
│ ├── visarb.png
│ ├── visarb.svg
│ ├── visarb2.png
│ ├── williams.png
│ └── williams2.png
├── package-lock.json
├── package.json
├── src
├── data
│ ├── 2025_changes.json
│ ├── contracts.json
│ ├── members.json
│ ├── news
│ │ ├── news_prompts_templates.json
│ │ └── news_titles_templates.json
│ └── records.json
├── index.html
├── index.js
├── js
│ ├── backend
│ │ ├── UESaveHandler.js
│ │ ├── UESaveTool
│ │ │ ├── Gvas.js
│ │ │ ├── GvasHeader.js
│ │ │ ├── LICENSE
│ │ │ ├── PropertyErrors.js
│ │ │ ├── Serializer.js
│ │ │ ├── arrays
│ │ │ │ ├── IntArray.js
│ │ │ │ ├── SoftObjectArray.js
│ │ │ │ ├── StructArray.js
│ │ │ │ └── index.js
│ │ │ ├── factories.js
│ │ │ ├── index.js
│ │ │ └── properties
│ │ │ │ ├── ArrayProperty.js
│ │ │ │ ├── BoolProperty.js
│ │ │ │ ├── EnumProperty.js
│ │ │ │ ├── FloatProperty.js
│ │ │ │ ├── Guid.js
│ │ │ │ ├── Int16Property.js
│ │ │ │ ├── Int64Property.js
│ │ │ │ ├── Int8Property.js
│ │ │ │ ├── IntProperty.js
│ │ │ │ ├── ObjectProperty.js
│ │ │ │ ├── Property.js
│ │ │ │ ├── SoftObjectProperty.js
│ │ │ │ ├── StrProperty.js
│ │ │ │ ├── StrProperty_.js
│ │ │ │ ├── StructProperty.js
│ │ │ │ ├── Tuple.js
│ │ │ │ └── index.js
│ │ ├── command.js
│ │ ├── commandGlobals.js
│ │ ├── dbManager.js
│ │ ├── scriptUtils
│ │ │ ├── calendarUtils.js
│ │ │ ├── carAnalysisUtils.js
│ │ │ ├── carConstants.js
│ │ │ ├── countries.js
│ │ │ ├── dbUtils.js
│ │ │ ├── editTeamUtils.js
│ │ │ ├── eidtStatsUtils.js
│ │ │ ├── head2head.js
│ │ │ ├── modUtils.js
│ │ │ ├── newsUtils.js
│ │ │ ├── recordUtils.js
│ │ │ ├── transferUtils.js
│ │ │ └── triggerUtils.js
│ │ └── worker.js
│ └── frontend
│ │ ├── calendar.js
│ │ ├── config.js
│ │ ├── dragFile.js
│ │ ├── head2head.js
│ │ ├── news.js
│ │ ├── performance.js
│ │ ├── public_key.js
│ │ ├── renderer.js
│ │ ├── seasonViewer.js
│ │ ├── stats.js
│ │ ├── teams.js
│ │ └── transfers.js
└── styles.css
├── vercel.json
└── webpack.config.js
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: f1dbeditor
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
13 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: "[BUG]"
5 | labels: bug
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Save file and log.txt**
24 | Attach a link to any file transfer web where you sharen your save and log.txt (located in the same folder as your save in the DBEditor) so I can try to reproduce the bug
25 |
26 | **Additional context**
27 | Add any other context about the problem here.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: "[FEATURE]"
5 | labels: enhancement
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/nightly.yml:
--------------------------------------------------------------------------------
1 | name: Nightly Vercel Deploy
2 | on:
3 | schedule:
4 | - cron: '0 2 * * *' # 02:00 UTC → 03:00/04:00 en Madrid según DST
5 | workflow_dispatch: # para probarlo a mano
6 |
7 | jobs:
8 | trigger:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Trigger Vercel Deploy Hook
12 | run: curl -sSf -X POST "$VERCEL_DEPLOY_HOOK_URL"
13 | env:
14 | VERCEL_DEPLOY_HOOK_URL: ${{ secrets.VERCEL_DEPLOY_HOOK_URL }}
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | result
3 | licenses
4 | backup
5 | log.txt
6 | **/build/back
7 | **/__pycache__
8 | *.sav
9 | configs/*.json
10 | DBEditor
11 | assets/custom/*.png
12 | assets/custom/*.jpg
13 | assets/custom/*.jpeg
14 | !base24_config.json
15 | dist/
16 | patreon/
17 | *.txt
18 | resize_images.js
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | Friendly to use tool for editing your save files from F1 Manager 24 and 23. Supports driver transfers, calendar customization, staff stat editing and car performance editing, and much more!
4 |
5 | **NOW ON THE WEB**: The DB Editor is now fully online! No more installations or stuff like that! Just get in, and edit. Everything. Everywhere.
6 |
7 | ## What's the Database Editor? ##
8 | Friendly user interface that helps you make the modifications you wish for the database from your save file from F1 Manager 24 and 23!
9 |
10 | ### What can I actually do with the Database Editor? ###
11 |
12 | 1. **DRIVERS** AND **STAFF** transfers
13 |
14 |
15 |
16 | 2. Editing driver/staff contracts, and adding contracts for the future
17 |
18 |
19 |
20 | 3. Check out what drivers/staff have pre-contracts signed (see Russell, Tsolov and Marti with then notification icon)
21 |
22 |
23 |
24 | 4. Edit driver numbers
25 | 5. Editing all staff (including drivers') stats, plus mentality and marketability, and **RENAME** any of them (including abreviations for drivers)
26 |
27 |
28 |
29 | 6. Customize your calendar, including weather of races
30 |
31 |
32 |
33 | 7. Make your save **HARDER** with the new difficulty presets (customizable)
34 | 8. **FREEZE THE MENTALITY** if you don't want that system interfering with your gameplay
35 | 9. Fix the AI **NOT REFURBISHING** their facilities
36 | 10. Change teams whenever you want
37 |
38 |
39 |
40 | 11. Edit every stat from all car parts from all teams on the grid
41 |
42 | 12. View every car performance across the season and compare each car attribute
43 |
44 |
45 |
46 |
47 | 13. Edit the performance and durability of any of the 4 engines suppliers
48 |
49 |
50 |
51 | 14. Add NEW CUSTOM ENGINES
52 |
53 |
54 |
55 | 15. Edit all 10 teams' facilities, money, budget available from the budget cap, long and short term objectives, enigne supplier and pit crew attributes
56 |
57 |
58 |
59 | 16. Keep track of past seasons with a wikipedia-style table for drivers and teams championships
60 |
61 |
62 |
63 | 17. Check records (wins, poles, podiums, WDCs) from previous seasons or compare them with all-time greats
64 |
65 |
66 |
67 | 18. Compare drivers and teamas and their stats from their past seasons using the Head To Head graphic
68 |
69 |
70 |
71 | 19. Follow stories from your save with the **NEWS TAB**
72 |
73 |
74 |
75 |
76 |
77 | > [!CAUTION]
78 | > For editing the **ORDER OR NUMBER** of races it is still recommended to only do it once before the first race of the season.
79 | > Editing the weather of races can be done whenever you want though
80 |
81 |
82 | ### How can I update the Database Editor if I had a previouos verision? ###
83 |
84 | You don'tn have to do anything! Since it runs now on the web it will be automaticallyn updated whnever I push a new change!
85 |
86 | ### Credits ###
87 |
88 | [xAranaktu for the save repacker](https://github.com/xAranaktu/F1-Manager-2022-SaveFile-Repacker)
89 |
--------------------------------------------------------------------------------
/assets/custom/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/custom/.gitkeep
--------------------------------------------------------------------------------
/assets/fonts/Formula1-Bold.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/fonts/Formula1-Bold.ttf
--------------------------------------------------------------------------------
/assets/fonts/Formula1-Regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/fonts/Formula1-Regular.ttf
--------------------------------------------------------------------------------
/assets/fonts/NewNumbers.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/fonts/NewNumbers.otf
--------------------------------------------------------------------------------
/assets/images/AMO.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/Antonelli4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/Antonelli4.png
--------------------------------------------------------------------------------
/assets/images/Hamilton4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/Hamilton4.png
--------------------------------------------------------------------------------
/assets/images/KA-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/KA-min.png
--------------------------------------------------------------------------------
/assets/images/LH-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/LH-min.png
--------------------------------------------------------------------------------
/assets/images/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/Logo.png
--------------------------------------------------------------------------------
/assets/images/MV-min.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/MV-min.png
--------------------------------------------------------------------------------
/assets/images/Verstappen4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/Verstappen4.png
--------------------------------------------------------------------------------
/assets/images/alfaromeo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/alfaromeo.png
--------------------------------------------------------------------------------
/assets/images/alphatauri.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/alphatauri.png
--------------------------------------------------------------------------------
/assets/images/alphatauri2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/alphatauri2.png
--------------------------------------------------------------------------------
/assets/images/alpine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/alpine.png
--------------------------------------------------------------------------------
/assets/images/alpine2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/alpine2.png
--------------------------------------------------------------------------------
/assets/images/andretti.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/andretti.png
--------------------------------------------------------------------------------
/assets/images/andretti2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/andretti2.png
--------------------------------------------------------------------------------
/assets/images/astonMartin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/astonMartin.png
--------------------------------------------------------------------------------
/assets/images/audi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/audi.png
--------------------------------------------------------------------------------
/assets/images/australia.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/australia.png
--------------------------------------------------------------------------------
/assets/images/austria.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/austria.png
--------------------------------------------------------------------------------
/assets/images/azerbaiyan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/azerbaiyan.png
--------------------------------------------------------------------------------
/assets/images/bahrain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/bahrain.png
--------------------------------------------------------------------------------
/assets/images/balgium.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/balgium.png
--------------------------------------------------------------------------------
/assets/images/brawn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/brawn.png
--------------------------------------------------------------------------------
/assets/images/brawn2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/brawn2.png
--------------------------------------------------------------------------------
/assets/images/brazil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/brazil.png
--------------------------------------------------------------------------------
/assets/images/canada.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/canada.png
--------------------------------------------------------------------------------
/assets/images/car.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/car.png
--------------------------------------------------------------------------------
/assets/images/china.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/china.png
--------------------------------------------------------------------------------
/assets/images/engine.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/engine.png
--------------------------------------------------------------------------------
/assets/images/ferrari.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/ferrari.png
--------------------------------------------------------------------------------
/assets/images/gbr.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/gbr.png
--------------------------------------------------------------------------------
/assets/images/haas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/haas.png
--------------------------------------------------------------------------------
/assets/images/headAero.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/headAero.png
--------------------------------------------------------------------------------
/assets/images/hugoboss.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/hugoboss.png
--------------------------------------------------------------------------------
/assets/images/hugoboss2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/hugoboss2.png
--------------------------------------------------------------------------------
/assets/images/hungry.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/hungry.png
--------------------------------------------------------------------------------
/assets/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/icon.png
--------------------------------------------------------------------------------
/assets/images/icon2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/icon2.png
--------------------------------------------------------------------------------
/assets/images/italy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/italy.png
--------------------------------------------------------------------------------
/assets/images/japan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/japan.png
--------------------------------------------------------------------------------
/assets/images/kick.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/kick.png
--------------------------------------------------------------------------------
/assets/images/kick2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/kick2.png
--------------------------------------------------------------------------------
/assets/images/kick3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/kick3.png
--------------------------------------------------------------------------------
/assets/images/logo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/logo2.png
--------------------------------------------------------------------------------
/assets/images/logoAlter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/logoAlter.png
--------------------------------------------------------------------------------
/assets/images/logoAlter.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
32 |
--------------------------------------------------------------------------------
/assets/images/logoVector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/logoVector.png
--------------------------------------------------------------------------------
/assets/images/logoVector3d.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/logoVector3d.png
--------------------------------------------------------------------------------
/assets/images/lotus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/lotus.png
--------------------------------------------------------------------------------
/assets/images/lotus2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/lotus2.png
--------------------------------------------------------------------------------
/assets/images/mclaren.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/mclaren.png
--------------------------------------------------------------------------------
/assets/images/mclaren2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/mclaren2.png
--------------------------------------------------------------------------------
/assets/images/mercedes.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/mercedes.png
--------------------------------------------------------------------------------
/assets/images/mexico.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/mexico.png
--------------------------------------------------------------------------------
/assets/images/modback.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/modback.png
--------------------------------------------------------------------------------
/assets/images/monaco.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/monaco.png
--------------------------------------------------------------------------------
/assets/images/ned.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/ned.png
--------------------------------------------------------------------------------
/assets/images/news/102_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/102_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/105_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/105_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/107_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/107_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/10_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/10_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/116_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/116_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/11_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/11_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/12_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/12_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/13_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/13_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/142_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/142_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/144_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/144_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/14_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/14_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/15_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/15_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/17_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/17_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/18_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/18_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/1_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/1_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/1_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/1_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/23_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/23_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/242_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/242_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/245_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/245_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/255_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/255_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/279_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/279_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/286_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/286_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/2_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/2_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/2_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/2_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/373_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/373_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/376_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/376_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/3_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/3_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/3_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/3_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/4_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/4_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/5_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/5_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/6_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/6_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/6_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/6_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/77_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/77_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/7_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/7_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/81_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/81_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/83_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/83_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/87_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/87_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/8_shot.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/8_shot.webp
--------------------------------------------------------------------------------
/assets/images/news/95_pad.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/95_pad.webp
--------------------------------------------------------------------------------
/assets/images/news/af1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/af1.webp
--------------------------------------------------------------------------------
/assets/images/news/af2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/af2.webp
--------------------------------------------------------------------------------
/assets/images/news/af_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/af_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/al1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/al1.webp
--------------------------------------------------------------------------------
/assets/images/news/al2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/al2.webp
--------------------------------------------------------------------------------
/assets/images/news/al_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/al_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/as1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/as1.webp
--------------------------------------------------------------------------------
/assets/images/news/as2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/as2.webp
--------------------------------------------------------------------------------
/assets/images/news/as_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/as_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/at1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/at1.webp
--------------------------------------------------------------------------------
/assets/images/news/at2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/at2.webp
--------------------------------------------------------------------------------
/assets/images/news/at_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/at_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/aus.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aus.webp
--------------------------------------------------------------------------------
/assets/images/news/aus_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aus_car.webp
--------------------------------------------------------------------------------
/assets/images/news/aus_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aus_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/aut.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aut.webp
--------------------------------------------------------------------------------
/assets/images/news/aut_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aut_car.webp
--------------------------------------------------------------------------------
/assets/images/news/aut_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aut_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/aze.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aze.webp
--------------------------------------------------------------------------------
/assets/images/news/aze_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aze_car.webp
--------------------------------------------------------------------------------
/assets/images/news/aze_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/aze_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/bah.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bah.webp
--------------------------------------------------------------------------------
/assets/images/news/bah_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bah_car.webp
--------------------------------------------------------------------------------
/assets/images/news/bah_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bah_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/bel.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bel.webp
--------------------------------------------------------------------------------
/assets/images/news/bel_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bel_car.webp
--------------------------------------------------------------------------------
/assets/images/news/bel_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bel_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/bra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bra.webp
--------------------------------------------------------------------------------
/assets/images/news/bra_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bra_car.webp
--------------------------------------------------------------------------------
/assets/images/news/bra_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/bra_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/can.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/can.webp
--------------------------------------------------------------------------------
/assets/images/news/can_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/can_car.webp
--------------------------------------------------------------------------------
/assets/images/news/can_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/can_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/champ1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/champ1.webp
--------------------------------------------------------------------------------
/assets/images/news/champ2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/champ2.webp
--------------------------------------------------------------------------------
/assets/images/news/champ3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/champ3.webp
--------------------------------------------------------------------------------
/assets/images/news/champ4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/champ4.webp
--------------------------------------------------------------------------------
/assets/images/news/champ5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/champ5.webp
--------------------------------------------------------------------------------
/assets/images/news/chi.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/chi.webp
--------------------------------------------------------------------------------
/assets/images/news/chi_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/chi_car.webp
--------------------------------------------------------------------------------
/assets/images/news/chi_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/chi_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/con1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con1.webp
--------------------------------------------------------------------------------
/assets/images/news/con2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con2.webp
--------------------------------------------------------------------------------
/assets/images/news/con3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con3.webp
--------------------------------------------------------------------------------
/assets/images/news/con4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con4.webp
--------------------------------------------------------------------------------
/assets/images/news/con5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con5.webp
--------------------------------------------------------------------------------
/assets/images/news/con6.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con6.webp
--------------------------------------------------------------------------------
/assets/images/news/con7.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con7.webp
--------------------------------------------------------------------------------
/assets/images/news/con8.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con8.webp
--------------------------------------------------------------------------------
/assets/images/news/con9.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/con9.webp
--------------------------------------------------------------------------------
/assets/images/news/ct_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ct_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/fe1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe1.webp
--------------------------------------------------------------------------------
/assets/images/news/fe2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe2.webp
--------------------------------------------------------------------------------
/assets/images/news/fe3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe3.webp
--------------------------------------------------------------------------------
/assets/images/news/fe4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe4.webp
--------------------------------------------------------------------------------
/assets/images/news/fe5.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe5.webp
--------------------------------------------------------------------------------
/assets/images/news/fe_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/fe_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/gbr.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/gbr.webp
--------------------------------------------------------------------------------
/assets/images/news/gbr_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/gbr_car.webp
--------------------------------------------------------------------------------
/assets/images/news/gbr_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/gbr_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/ha1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ha1.webp
--------------------------------------------------------------------------------
/assets/images/news/ha2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ha2.webp
--------------------------------------------------------------------------------
/assets/images/news/ha_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ha_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/hun.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/hun.webp
--------------------------------------------------------------------------------
/assets/images/news/hun_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/hun_car.webp
--------------------------------------------------------------------------------
/assets/images/news/hun_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/hun_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/imo.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/imo.webp
--------------------------------------------------------------------------------
/assets/images/news/imo_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/imo_car.webp
--------------------------------------------------------------------------------
/assets/images/news/imo_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/imo_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/ita.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ita.webp
--------------------------------------------------------------------------------
/assets/images/news/ita_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ita_car.webp
--------------------------------------------------------------------------------
/assets/images/news/ita_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ita_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/jap.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/jap.webp
--------------------------------------------------------------------------------
/assets/images/news/jap_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/jap_car.webp
--------------------------------------------------------------------------------
/assets/images/news/jap_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/jap_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/mc1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mc1.webp
--------------------------------------------------------------------------------
/assets/images/news/mc2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mc2.webp
--------------------------------------------------------------------------------
/assets/images/news/mc_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mc_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/me1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/me1.webp
--------------------------------------------------------------------------------
/assets/images/news/me2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/me2.webp
--------------------------------------------------------------------------------
/assets/images/news/me3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/me3.webp
--------------------------------------------------------------------------------
/assets/images/news/me4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/me4.webp
--------------------------------------------------------------------------------
/assets/images/news/me_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/me_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/mex.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mex.webp
--------------------------------------------------------------------------------
/assets/images/news/mex_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mex_car.webp
--------------------------------------------------------------------------------
/assets/images/news/mex_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mex_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/mia.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mia.webp
--------------------------------------------------------------------------------
/assets/images/news/mia_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mia_car.webp
--------------------------------------------------------------------------------
/assets/images/news/mia_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mia_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/mon.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mon.webp
--------------------------------------------------------------------------------
/assets/images/news/mon_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mon_car.webp
--------------------------------------------------------------------------------
/assets/images/news/mon_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/mon_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/ned.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ned.webp
--------------------------------------------------------------------------------
/assets/images/news/ned_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ned_car.webp
--------------------------------------------------------------------------------
/assets/images/news/ned_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/ned_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/qat.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/qat.webp
--------------------------------------------------------------------------------
/assets/images/news/qat_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/qat_car.webp
--------------------------------------------------------------------------------
/assets/images/news/qat_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/qat_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/rb1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/rb1.webp
--------------------------------------------------------------------------------
/assets/images/news/rb2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/rb2.webp
--------------------------------------------------------------------------------
/assets/images/news/rb3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/rb3.webp
--------------------------------------------------------------------------------
/assets/images/news/rb4.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/rb4.webp
--------------------------------------------------------------------------------
/assets/images/news/rb_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/rb_factory.webp
--------------------------------------------------------------------------------
/assets/images/news/sau.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sau.webp
--------------------------------------------------------------------------------
/assets/images/news/sau_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sau_car.webp
--------------------------------------------------------------------------------
/assets/images/news/sau_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sau_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/sgp.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sgp.webp
--------------------------------------------------------------------------------
/assets/images/news/sgp_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sgp_car.webp
--------------------------------------------------------------------------------
/assets/images/news/sgp_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/sgp_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/spa.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/spa.webp
--------------------------------------------------------------------------------
/assets/images/news/spa_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/spa_car.webp
--------------------------------------------------------------------------------
/assets/images/news/spa_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/spa_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/uae.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/uae.webp
--------------------------------------------------------------------------------
/assets/images/news/uae_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/uae_car.webp
--------------------------------------------------------------------------------
/assets/images/news/uae_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/uae_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/usa.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/usa.webp
--------------------------------------------------------------------------------
/assets/images/news/usa_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/usa_car.webp
--------------------------------------------------------------------------------
/assets/images/news/usa_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/usa_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/veg.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/veg.webp
--------------------------------------------------------------------------------
/assets/images/news/veg_car.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/veg_car.webp
--------------------------------------------------------------------------------
/assets/images/news/veg_tra.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/veg_tra.webp
--------------------------------------------------------------------------------
/assets/images/news/wi1.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/wi1.webp
--------------------------------------------------------------------------------
/assets/images/news/wi2.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/wi2.webp
--------------------------------------------------------------------------------
/assets/images/news/wi3.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/wi3.webp
--------------------------------------------------------------------------------
/assets/images/news/wi_factory.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/news/wi_factory.webp
--------------------------------------------------------------------------------
/assets/images/patreonLogo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/placeholder.png
--------------------------------------------------------------------------------
/assets/images/porsche.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/porsche.png
--------------------------------------------------------------------------------
/assets/images/qatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/qatar.png
--------------------------------------------------------------------------------
/assets/images/raceEngineer2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/raceEngineer2.png
--------------------------------------------------------------------------------
/assets/images/redbull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/redbull.png
--------------------------------------------------------------------------------
/assets/images/renault.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/renault.png
--------------------------------------------------------------------------------
/assets/images/renault2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/renault2.png
--------------------------------------------------------------------------------
/assets/images/sauber.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/sauber.png
--------------------------------------------------------------------------------
/assets/images/sauber2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/sauber2.png
--------------------------------------------------------------------------------
/assets/images/saudi.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/saudi.jpg
--------------------------------------------------------------------------------
/assets/images/singapore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/singapore.png
--------------------------------------------------------------------------------
/assets/images/spain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/spain.png
--------------------------------------------------------------------------------
/assets/images/splash-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/splash-logo.png
--------------------------------------------------------------------------------
/assets/images/sportingDirector.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/sportingDirector.png
--------------------------------------------------------------------------------
/assets/images/technicalChief.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/technicalChief.png
--------------------------------------------------------------------------------
/assets/images/thumb.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/assets/images/toyota.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/toyota.png
--------------------------------------------------------------------------------
/assets/images/toyota2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/toyota2.png
--------------------------------------------------------------------------------
/assets/images/uae.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/uae.png
--------------------------------------------------------------------------------
/assets/images/usa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/usa.png
--------------------------------------------------------------------------------
/assets/images/visarb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/visarb.png
--------------------------------------------------------------------------------
/assets/images/visarb2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/visarb2.png
--------------------------------------------------------------------------------
/assets/images/williams.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/williams.png
--------------------------------------------------------------------------------
/assets/images/williams2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IUrreta/DatabaseEditor/f97e6b8bfb3cf38e62fa3cfe58ee452768466f11/assets/images/williams2.png
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Database Editor F1 Manager",
3 | "version": "3.2.4",
4 | "description": "A tool that will let you edit your save file from F1 Manager games",
5 | "main": "main.js",
6 | "scripts": {
7 | "build": "webpack"
8 | },
9 | "repository": "https://github.com/IUrreta/DatabaseEditor",
10 | "author": "IUrreta",
11 | "license": "LGPL-3.0-or-later",
12 | "dependencies": {
13 | "@google/genai": "^0.12.0",
14 | "@vercel/analytics": "^1.5.0",
15 | "@vercel/speed-insights": "^1.2.0",
16 | "babel-loader": "^9.2.1",
17 | "bootstrap": "^5.3.3",
18 | "bootstrap-icons": "^1.11.0",
19 | "buffer": "^6.0.3",
20 | "chart.js": "^4.4.0",
21 | "chartjs-plugin-annotation": "^3.0.1",
22 | "chartjs-plugin-datalabels": "^2.2.0",
23 | "crypto-browserify": "^3.12.1",
24 | "file-saver": "^2.0.5",
25 | "interactjs": "^1.10.17",
26 | "marked": "^7.0.3",
27 | "pako": "^2.1.0",
28 | "simple-git": "^3.19.1",
29 | "sql.js": "^1.12.0"
30 | },
31 | "devDependencies": {
32 | "autoprefixer": "^10.4.20",
33 | "copy-webpack-plugin": "^12.0.2",
34 | "css-loader": "^7.1.2",
35 | "csv-parser": "^3.2.0",
36 | "html-webpack-plugin": "^5.6.3",
37 | "image-minimizer-webpack-plugin": "^4.1.3",
38 | "mini-css-extract-plugin": "^2.9.2",
39 | "postcss-loader": "^8.1.1",
40 | "sass": "^1.84.0",
41 | "sass-loader": "^16.0.4",
42 | "sharp": "^0.33.5",
43 | "style-loader": "^4.0.0",
44 | "webpack": "^5.97.1",
45 | "webpack-cli": "^6.0.1"
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/data/members.json:
--------------------------------------------------------------------------------
1 | [
2 | {"name":"Viggo","tier":"backer"},
3 | {"name":"The Logical Bass Fish","tier":"backer"},
4 | {"name":"Kymani Clark","tier":"backer"},
5 | {"name":"zagrebelnio","tier":"backer"},
6 | {"name":"Cian In 't Holt","tier":"founder"},
7 | {"name":"Alessio Romeo","tier":"backer"},
8 | {"name":"Stuka Boy","tier":"backer"},
9 | {"name":"Dave","tier":"backer"},
10 | {"name":"Jaume Carbonell","tier":"founder"},
11 | {"name":"JO96","tier":"backer"},
12 | {"name":"Fynn Lemke","tier":"backer"},
13 | {"name":"Evangelos Boukouris","tier":"backer"},
14 | {"name":"alfonso castillo lemus","tier":"backer"},
15 | {"name":"Diogo","tier":"backer"},
16 | {"name":"Rob","tier":"backer"},
17 | {"name":"Niklas","tier":"backer"},
18 | {"name":"Coen Duggan","tier":"backer"},
19 | {"name":"Flo lp64","tier":"backer"},
20 | {"name":"World By Adam","tier":"backer"},
21 | {"name":"Cardemir","tier":"backer"},
22 | {"name":"Florian Paul","tier":"backer"},
23 | {"name":"Kakub","tier":"backer"},
24 | {"name":"Håvard Fredriksen","tier":"backer"},
25 | {"name":"WHO IS MINICO?","tier":"backer"},
26 | {"name":"Daniel Esterson","tier":"backer"},
27 | {"name":"Lyliaan","tier":"backer"},
28 | {"name":"Lukus Wright","tier":"backer"},
29 | {"name":"retrop","tier":"backer"},
30 | {"name":"Duarte Sardinha","tier":"backer"},
31 | {"name":"Jose","tier":"backer"},
32 | {"name":"Kevin Peltser","tier":"supporter"},
33 | {"name":"Xabier","tier":"backer"},
34 | {"name":"Khan Goatman","tier":"backer"},
35 | {"name":"ccesvin","tier":"backer"},
36 | {"name":"Artyom Sizov","tier":"supporter"},
37 | {"name":"Commando1180","tier":"backer"},
38 | {"name":"영민 이","tier":"backer"},
39 | {"name":"Happy Oriley","tier":"backer"},
40 | {"name":"Alexander Udeaja","tier":"backer"},
41 | {"name":"Mackle","tier":"backer"},
42 | {"name":"Eetu Väisänen","tier":"backer"},
43 | {"name":"용준 김","tier":"backer"},
44 | {"name":"Kevin Szilagyi","tier":"backer"},
45 | {"name":"스tv 빅","tier":"supporter"},
46 | {"name":"Jack Sives","tier":"backer"},
47 | {"name":"Kunal Shah","tier":"supporter"},
48 | {"name":"Wan Muhammad","tier":"supporter"},
49 | {"name":"aalyx","tier":"backer"},
50 | {"name":"JARNCJ","tier":"backer"},
51 | {"name":"Shadow 2Jz 99","tier":"backer"},
52 | {"name":"Vladuna","tier":"backer"},
53 | {"name":"Tom","tier":"backer"},
54 | {"name":"Schnupsi Dupsi","tier":"supporter"},
55 | {"name":"Ian Ang","tier":"backer"},
56 | {"name":"ysty04","tier":"founder"},
57 | {"name":"선호 김","tier":"supporter"},
58 | {"name":"Bruno Piris","tier":"backer"},
59 | {"name":"YannAsdelion","tier":"supporter"},
60 | {"name":"Eee","tier":"backer"},
61 | {"name":"海宝賢一郎","tier":"supporter"},
62 | {"name":"Brian Ferguson","tier":"backer"},
63 | {"name":"Michael","tier":"backer"},
64 | {"name":"RageEliteX","tier":"supporter"},
65 | {"name":"Ericthebest","tier":"supporter"},
66 | {"name":"Djocyk","tier":"supporter"},
67 | {"name":"alex browne","tier":"supporter"},
68 | {"name":"david garcia","tier":"supporter"},
69 | {"name":"Joshua Herreen","tier":"backer"},
70 | {"name":"SpeedCB28","tier":"backer"},
71 | {"name":"Q9R","tier":"supporter"},
72 | {"name":"Pepelino55","tier":"backer"},
73 | {"name":"gspintel.","tier":"supporter"},
74 | {"name":"MSC_SchumiWM2022","tier":"backer"},
75 | {"name":"Kold","tier":"founder"},
76 | {"name":"Darren2ickn9r","tier":"founder"},
77 | {"name":"Jaume sl23","tier":"supporter"},
78 | {"name":"Michael Parchaiski","tier":"backer"},
79 | {"name":"Martin Schwingeweitzen","tier":"supporter"},
80 | {"name":"Friedemann Der 1.","tier":"backer"},
81 | {"name":"Billy Manson","tier":"backer"},
82 | {"name":"matteo fournier","tier":"backer"},
83 | {"name":"Abner Chao","tier":"backer"},
84 | {"name":"katon","tier":"backer"},
85 | {"name":"Wild Buzzer","tier":"backer"},
86 | {"name":"LilHowza","tier":"supporter"},
87 | {"name":"Carl Robinson","tier":"backer"},
88 | {"name":"Anthony D'Amico","tier":"supporter"},
89 | {"name":"Matej Lhotsky","tier":"backer"},
90 | {"name":"Sarah Gregory","tier":"backer"},
91 | {"name":"Bailey Sprecher","tier":"backer"},
92 | {"name":"metin","tier":"backer"},
93 | {"name":"Michael Gabriel","tier":"founder"},
94 | {"name":"Nuttybong","tier":"backer"},
95 | {"name":"Honk_04","tier":"supporter"},
96 | {"name":"LGFT","tier":"backer"},
97 | {"name":"Delay Delama","tier":"founder"},
98 | {"name":"Odradek","tier":"backer"},
99 | {"name":"Leikjarinn","tier":"founder"},
100 | {"name":"Erik Spiering","tier":"founder"},
101 | {"name":"JKing","tier":"backer"},
102 | {"name":"Thomas","tier":"backer"},
103 | {"name":"Brandon","tier":"backer"},
104 | {"name":"Maikel Zwart","tier":"supporter"},
105 | {"name":"William Slocombe","tier":"backer"},
106 | {"name":"Shaun Thomas","tier":"backer"},
107 | {"name":"Hadock","tier":"backer"},
108 | {"name":"Joe","tier":"supporter"},
109 | {"name":"Robin Rößler","tier":"supporter"},
110 | {"name":"lee","tier":"backer"},
111 | {"name":"Jack Alexander Naylor","tier":"backer"},
112 | {"name":"Richard Hicks","tier":"founder"},
113 | {"name":"BleedinEdge821","tier":"backer"},
114 | {"name":"Kyle","tier":"supporter"},
115 | {"name":"Old Mate Johno","tier":"backer"},
116 | {"name":"Justin Gärtner","tier":"backer"},
117 | {"name":"Krispy~","tier":"backer"},
118 | {"name":"Sabrina Almer","tier":"backer"},
119 | {"name":"Patryk Gadomski","tier":"founder"},
120 | {"name":"aarava","tier":"supporter"},
121 | {"name":"MickHulst","tier":"backer"},
122 | {"name":"Cm382714","tier":"backer"},
123 | {"name":"Robbles Quin","tier":"backer"},
124 | {"name":"Qingou Liu","tier":"backer"},
125 | {"name":"Kevin Brandsborg","tier":"backer"},
126 | {"name":"Kevin Brogan","tier":"supporter"},
127 | {"name":"Robin","tier":"supporter"},
128 | {"name":"Axel Ravix","tier":"backer"},
129 | {"name":"Cole Pelzer","tier":"backer"},
130 | {"name":"kotemen89","tier":"backer"},
131 | {"name":"Robin Frisk","tier":"backer"},
132 | {"name":"senweg888","tier":"founder"},
133 | {"name":"Jack Matthews","tier":"backer"},
134 | {"name":"lovetoojacket","tier":"supporter"},
135 | {"name":"Ryan","tier":"supporter"},
136 | {"name":"Ross Patterson","tier":"backer"},
137 | {"name":"Mark Wright","tier":"backer"},
138 | {"name":"Sam_Fakt","tier":"founder"},
139 | {"name":"Enhabe","tier":"backer"},
140 | {"name":"Marcus Miller","tier":"founder"},
141 | {"name":"Oliver Nelson","tier":"supporter"},
142 | {"name":"FirearmofMutiny","tier":"supporter"},
143 | {"name":"ollie","tier":"backer"},
144 | {"name":"Harry Willetts","tier":"founder"},
145 | {"name":"Roberto Cruz","tier":"backer"},
146 | {"name":"Kaiowa McAninly","tier":"backer"},
147 | {"name":"Ethan Tapa","tier":"founder"},
148 | {"name":"tornado_ow","tier":"founder"},
149 | {"name":"Anthony Taylor","tier":"backer"},
150 | {"name":"averstance","tier":"backer"},
151 | {"name":"Justin Logan","tier":"backer"},
152 | {"name":"Noel Hernandez","tier":"founder"},
153 | {"name":"Peregrin","tier":"founder"},
154 | {"name":"Jayden Martis","tier":"backer"},
155 | {"name":"Marcus Deamon","tier":"backer"},
156 | {"name":"Aaron655","tier":"founder"},
157 | {"name":"Parker Petrov","tier":"backer"}
158 | ]
--------------------------------------------------------------------------------
/src/data/news/news_prompts_templates.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "quali_result",
4 | "new_type": 1,
5 | "prompt": "Write ONLY the body (not the title) of a news article in English summarizing the qualifying session of the {{circuit}} Grand Prix in {{season_year}} where {{pole_driver}} got pole position. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Focus on the results of the qualifying session and the starting grid, highlighting the pole position and the top qualifiers. Mention the key moments that determined the final positions, if provided, but do not speculate beyond the data given. Describe how the pole-sitter achieved the fastest time and highlight any unexpected performances or upsets. Use a flowing, paragraph-based structure like a typical F1 news report."
6 | },
7 | {
8 | "id": "race_result",
9 | "new_type": 2,
10 | "prompt": "Write ONLY the body (not the title) of a news article in English summarizing the win that {{winner}} achieved at the {{circuit}} Grand Prix in {{season_year}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Focus on the race results and championship implications, but do not assume how dominant or dramatic the race was unless the gaps clearly suggest it. Mention the top finishers in a narrative way, without listing all drivers or repeating every time gap. Instead of exact time gaps with decimals, use natural phrases like “a few seconds ahead”, “closely followed by”, or “just behind”. Use a flowing, paragraph-based structure like a typical F1 news report."
11 | },
12 | {
13 | "id": "silly_season_recap",
14 | "new_type": 4,
15 | "prompt": "Write ONLY the body (not the title) of a news article in English summarizing the current state of the {{season}} F1 silly season. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 300 and 400 words long. Focus on the driver market, mentioning key drivers who are rumored to be moving teams, and any confirmed moves. Discuss the implications of these changes for the teams and drivers involved. When talking about salaries, do not give overly specific figures, but rather discuss approximate amounts with a round number. Vary the phrasing when describing which seats are being targeted, so it does not sound repetitive. Use a flowing, paragraph-based structure like a typical F1 news report."
16 | },
17 | {
18 | "id": "made_up_rumor",
19 | "new_type": 7,
20 | "prompt": "Write ONLY the body (not the title) of a news article in English covering a rumor about {{driver1}} potentially leaving {{team1}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Clearly indicate that the information is speculative and based on paddock gossip. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Discuss why this rumor has surfaced, mentioning any factors that might contribute to {{driver1}} considering a move. Highlight any potential destinations that have been suggested, but avoid stating anything as confirmed. Balance the tone between curiosity and skepticism, as the rumor has not been verified. Use a flowing, paragraph-based structure like a typical F1 news report."
21 | },
22 | {
23 | "id": "big_transfer",
24 | "new_type": 6,
25 | "prompt": "Write ONLY the body (not the title) of a news article in English confirming the big transfer of {{driver1}} to {{team1}} from {{team2}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Focus on the details of the transfer, including the reasons behind it and the implications for both the driver and the team. Use a flowing, paragraph-based structure like a typical F1 news report."
26 | },
27 | {
28 | "id": "potential_champion",
29 | "new_type": 8,
30 | "prompt": "Write ONLY the body (not the title) of a news article in English about {{driver_name}} having the potential to become the {{season_year}} Formula 1 World Champion at the upcoming {{circuit}} Grand Prix. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Explain the conditions under which {{driver_name}} would secure the championship at this race, detailing the necessary points advantage over their closest rival, {{rival_driver_name}}, considering the points currently held by both ({{driver_points}} vs {{rival_points}}). Mention the maximum points available at this event (including any sprint race) and the number of races remaining after this one. Focus on what {{driver_name}} needs to achieve (e.g., win the race, finish in a certain position while {{rival_driver_name}} finishes outside another position)."
31 | },
32 | {
33 | "id": "world_champion",
34 | "new_type": 9,
35 | "prompt": "Write ONLY the body (not the title) of a news article in English about {{driver_name}} winning the {{season_year}} Formula 1 World Championship at the {{circuit}} Grand Prix. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Summarize how {{driver_name}} clinched the championship, detailing their performance throughout the season and the key moments that led to this achievement. Mention the final points tally, comparing it to their closest rival, {{rival_driver_name}}, and highlight any significant result differences. Discuss the implications of this victory for both {{driver_name}} and their team, including any records set or milestones achieved. Use a flowing, paragraph-based structure like a typical F1 news report."
36 | },
37 | {
38 | "id": "contract_renewal",
39 | "new_type": 10,
40 | "prompt": "Write ONLY the body (not the title) of a news article in English about {{driver1}} renewing their contract with {{team1}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 200 and 310 words long. Focus on the details of the contract renewal, including the duration of the new contract and any improvements in terms or conditions. Discuss the reasons behind {{driver1}}'s decision to stay with {{team1}}, highlighting their performance and relationship with the team. Mention any statements from {{driver1}} or team representatives regarding the renewal. When talking about salaries, do not give overly specific figures, but rather discuss approximate amounts with a round number."
41 | },
42 | {
43 | "id": "team_comparison_bad",
44 | "new_type": 11,
45 | "prompt": "Write ONLY the body (not the title) of a news article in English about {{team1}}'s performance in the {{actualSeason}} Formula 1 season so far, comparing it to their better {{lastSeason}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 300 and 400 words long. Explain with all the info given why they are scoring fewer points this season. Use a structured but flowing narrative, as in a typical F1 news report."
46 | },
47 | {
48 | "id": "team_comparison_good",
49 | "new_type": 12,
50 | "prompt": "Write ONLY the body (not the title) of a news article in English about {{team1}}'s performance in the {{actualSeason}} Formula 1 season so far, comparing it to their worse {{lastSeason}}. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 300 and 400 words long. Explain with all the info given why they are scoring more points this season. Use a structured but flowing narrative, as in a typical F1 news report."
51 | },
52 | {
53 | "id": "driver_comparison",
54 | "new_type": 13,
55 | "prompt": "Write ONLY the body (not the title) of a news article in English comparing the performances of {{driver1}} and {{driver2}} from {{team1}} in the {{actualSeason}} Formula 1 season so far. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 300 and 400 words long. Analyze their performances based on the provided statistics, including points scored, qualifying positions, and race results. Compare how many times each driver has outqualified the other and their and how many times each has finished ahead of the other."
56 | },
57 | {
58 | "id": "season_review",
59 | "new_type": 14,
60 | "prompt": "Write ONLY the body (not the title) of a news article in English reviewing the {{season_year}} Formula 1 season so far, after the {{part}} third of the season has gone. {{driver1}} is leading the Drivers Championship followed by {{driver2}}, and {{team1}} is currently leading the Constructors Championship. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 400 and 500 words long. Summarize the key events of the season, including the championship battle, standout performances by drivers and teams, and any significant controversies or developments. Highlight the main storylines that defined the season and discuss their implications for the future of Formula 1. Use a flowing, paragraph-based structure like a typical F1 news report."
61 | },
62 | {
63 | "id": "season_review_end",
64 | "new_type": 15,
65 | "prompt": "Write ONLY the body (not the title) of a news article in English reviewing the recently concluded {{season_year}} Formula 1 season. {{driver1}} won the championship followed by {{driver2}}, and {{team1}} got the Constructors Championship. Use clear, natural English in a professional journalistic style, similar to outlets like Autosport or Motorsport.com. Avoid slang, overly dramatic phrasing, or unnecessary flourishes. The article should be between 500 and 650 words long. This article should mainly be focused on the best and worst performers of the season, both drivers and teams. Include your winners and losers, or your power rankings of the best and worst drivers and teams and why they delivered or underperformed. Discuss the main storylines that defined the season, including the championship battle, standout performances, and any significant controversies or developments. Use a flowing, paragraph-based structure like a typical F1 news report."
66 | }
67 | ]
68 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | // Bootstrap
2 | import "bootstrap/dist/css/bootstrap.min.css";
3 | import 'bootstrap-icons/font/bootstrap-icons.min.css';
4 |
5 | // Resto de imports
6 | import interact from 'interactjs';
7 |
8 | import Chart from 'chart.js/auto';
9 | import Annotation from "chartjs-plugin-annotation";
10 | import 'chartjs-plugin-annotation';
11 | import 'chartjs-plugin-datalabels';
12 |
13 | import './js/frontend/calendar.js';
14 | import './js/frontend/renderer.js';
15 | import './js/frontend/transfers.js';
16 | import './js/frontend/stats.js';
17 | import './js/frontend/performance.js';
18 | import './js/frontend/seasonViewer.js';
19 | import './js/frontend/head2head.js';
20 | import './js/frontend/teams.js';
21 | import './js/frontend/dragFile.js';
22 | import './js/frontend/news.js'
23 |
24 | import './styles.css';
--------------------------------------------------------------------------------
/src/js/backend/UESaveHandler.js:
--------------------------------------------------------------------------------
1 | import {Gvas, Serializer} from "./UESaveTool";
2 | import pako from "pako";
3 | import { saveAs } from "file-saver";
4 | import { Buffer } from "buffer";
5 |
6 | export const parseGvasProps = (Properties) => {
7 | const careerSaveMetadata = {};
8 | const metadataProperty = Properties.Properties.filter(x => x.Name === "MetaData")[0];
9 | const careerSaveMetadataProperty = metadataProperty.Properties[0];
10 |
11 | careerSaveMetadataProperty.Properties.forEach(prop => {
12 | careerSaveMetadata[prop.Name] = prop.Property || prop.Properties;
13 | })
14 |
15 | return { careerSaveMetadata };
16 | }
17 |
18 | export const analyzeFileToDatabase = async (file, SQL) => {
19 | return new Promise((resolve) => {
20 | if (file !== undefined) {
21 | let reader = new FileReader();
22 | reader.onload = async (e) => {
23 | const serial = new Serializer(Buffer.from(reader.result));
24 | const gvasMeta = new Gvas().deserialize(serial);
25 | const { Header, Properties } = gvasMeta;
26 | const { SaveGameVersion, EngineVersion } = Header;
27 | const { BuildId, Build } = EngineVersion;
28 | let version = 0, gameVersion, gameVersionWithBuild;
29 | switch (SaveGameVersion) {
30 | case 2:
31 | version = 2;
32 | gameVersion = BuildId.substring(BuildId.indexOf("22_") + 3);
33 | gameVersionWithBuild = `${gameVersion}.${Build & 0x7fffffff}`;
34 | break;
35 | case 3:
36 | if (BuildId.indexOf("volta23") !== -1) {
37 | version = 3;
38 | gameVersion = BuildId.substring(BuildId.indexOf("23+") + 3);
39 | gameVersionWithBuild = `${gameVersion}.${Build & 0x7fffffff}`;
40 | }
41 | if (BuildId.indexOf("volta24") !== -1) {
42 | version = 4;
43 | gameVersion = BuildId.substring(BuildId.indexOf("24+") + 8);
44 | gameVersionWithBuild = `${gameVersion}.${Build & 0x7fffffff}`;
45 | }
46 | break;
47 | default:
48 | version = 0;
49 | }
50 |
51 |
52 | const unk_zero = serial.readInt32();
53 | const total_size = serial.readInt32();
54 | const size_1 = serial.readInt32();
55 | const size_2 = serial.readInt32();
56 | const size_3 = serial.readInt32();
57 |
58 | const compressedData = serial.read(total_size);
59 | const output = pako.inflate(compressedData);
60 | const databaseFile = output.slice(0, size_1);
61 |
62 |
63 | const text = new TextDecoder().decode(databaseFile.slice(0, 16));
64 |
65 | // @ts-ignore
66 |
67 | const db = new SQL.Database(databaseFile);
68 |
69 | const metadata = {
70 | filename: file.name, // for in-app
71 |
72 | version,
73 | fullBuildId: BuildId,
74 | gameVersion,
75 | gameVersionWithBuild,
76 |
77 | databaseFile,
78 |
79 | gvasMeta,
80 | gvasHeader: Header, // read-only
81 |
82 | ...parseGvasProps(Properties),
83 |
84 | otherDatabases: [{
85 | size: size_2,
86 | file: output.slice(size_1, size_1 + size_2),
87 | }, {
88 | size: size_3,
89 | file: output.slice(size_1 + size_2, size_1 + size_2 + size_3),
90 | }]
91 | }
92 |
93 | if (process.env.NODE_ENV === 'development') {
94 | // saveAs(new Blob([metadata.chunk0], {type: "application/binary"}), "chunk0");
95 | }
96 |
97 | resolve({db, metadata});
98 | };
99 | reader.readAsArrayBuffer(file);
100 | }
101 | });
102 | }
103 |
104 | export const repack = (db, metadata, overwrite = false) => {
105 | const db_data = db.export();
106 | const db_size = db_data.length;
107 |
108 | const { otherDatabases, gvasMeta } = metadata;
109 |
110 | const s1 = otherDatabases[0].size;
111 | const s2 = otherDatabases[1].size;
112 |
113 | const compressedData = new Buffer(db_size + s1 + s2);
114 | compressedData.set(db_data, 0);
115 | compressedData.set(otherDatabases[0].file, db_size);
116 | compressedData.set(otherDatabases[1].file, db_size + s1);
117 |
118 | const compressed = pako.deflate(compressedData);
119 | const compressed_size = compressed.length;
120 |
121 | const serialized = gvasMeta.serialize();
122 | const meta_length = serialized.length;
123 |
124 | const check = new Gvas().deserialize(
125 | new Serializer(Buffer.from(serialized))
126 | );
127 |
128 | if (JSON.stringify(gvasMeta) === JSON.stringify(check)) {
129 | const finalData = new Buffer(meta_length + 16 + compressed_size);
130 |
131 | finalData.set(serialized, 0);
132 | finalData.writeInt32LE(compressed_size, meta_length);
133 | finalData.writeInt32LE(db_size, meta_length + 4);
134 | finalData.writeInt32LE(s1, meta_length + 8);
135 | finalData.writeInt32LE(s2, meta_length + 12);
136 | finalData.set(compressed, meta_length + 16);
137 |
138 | console.log("Repacked", finalData);
139 |
140 | return { finalData, metadata };
141 |
142 | saveAs(new Blob([finalData], {type: "application/binary"}), metadata.filename);
143 |
144 | } else {
145 | alert("Savefile Serialization Check failed.")
146 | }
147 |
148 | }
149 |
150 | export const dump = (db, metadata) => {
151 | saveAs(new Blob([db.export()], {type: "application/vnd.sqlite3"}), metadata.filename + ".db");
152 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/Gvas.js:
--------------------------------------------------------------------------------
1 | import {PropertyFactory} from './factories';
2 | import {GvasHeader} from "./GvasHeader";
3 | import {Tuple} from './properties';
4 | import {SerializationError} from './PropertyErrors';
5 | import {Serializer} from "./Serializer";
6 | import { Buffer } from "buffer";
7 |
8 | export class Gvas {
9 | constructor() {
10 | this.Header = new GvasHeader();
11 | this.Properties = new Tuple();
12 | }
13 | get Size() {
14 | let size = this.Header.Size;
15 | size += this.Properties.Size;
16 | size += 4;
17 | return size;
18 | }
19 | deserialize(serial) {
20 | let format = serial.read(4);
21 | if (Buffer.compare(Buffer.from('GVAS'), format) !== 0)
22 | throw Error(`Unexpected header, expected 'GVAS`)
23 | this.Header.deserialize(serial);
24 | this.Properties.Name = this.Header.SaveGameClassName;
25 | this.Properties.deserialize(serial);
26 | return this;
27 | }
28 | serialize() {
29 | let serial = Serializer.alloc(this.Size);
30 | serial.write(this.Header.serialize());
31 | serial.write(this.Properties.serialize());
32 | serial.seek(4);
33 | if (serial.tell !== this.Size)
34 | throw new SerializationError(this);
35 | return serial.Data;
36 | }
37 | static from(obj) {
38 | let gvas = new Gvas();
39 | gvas.Header = GvasHeader.from(obj.Header);
40 | gvas.Properties = PropertyFactory.create(obj.Properties);
41 | return gvas;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/GvasHeader.js:
--------------------------------------------------------------------------------
1 | import {SerializationError} from '.';
2 | import {PropertyFactory} from './factories';
3 | import {Serializer} from './Serializer';
4 | import { Buffer } from "buffer";
5 |
6 | export class GvasHeader {
7 | constructor() {
8 | this.Format = 'GVAS';
9 | this.SaveGameVersion = 0;
10 | this.PackageVersion = 0;
11 | this.PackageFileVersionUE5 = 0;
12 | this.EngineVersion = {
13 | Major: 0,
14 | Minor: 0,
15 | Patch: 0,
16 | Build: 0,
17 | BuildId: ""
18 | }
19 | this.CustomFormatVersion = 0;
20 | this.CustomFormatData = {
21 | Count: 0,
22 | Entries: []
23 | }
24 | this.SaveGameClassName = "";
25 | }
26 | get Size() {
27 | let size = this.Format.length;
28 | size += 18;
29 |
30 | size += this.EngineVersion.BuildId.length + 1 + 4;
31 | if (this.EngineVersion.Major >= 5) {
32 | size += 4;
33 | }
34 | size += 8;
35 | this.CustomFormatData.Entries.forEach(guid => {
36 | size += guid.Size; // 20
37 | })
38 | size += this.SaveGameClassName.length + 1 + 4;
39 | return size;
40 | }
41 | deserialize(serial) {
42 | /* 5.3: https://github.com/EpicGames/UnrealEngine/blob/5.3/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp#L85 */
43 |
44 | // FileTypeTag: GVAS
45 | this.SaveGameVersion = serial.readInt32();
46 | this.PackageVersion = serial.readInt32();
47 | if (this.SaveGameVersion >= 3) {
48 | this.PackageFileVersionUE5 = serial.readInt32();
49 | /* this needs to be larger than 1000 */
50 | }
51 | /*
52 | 3 means PackageFileSummaryVersionChange, rather than F1M 2023
53 | https://github.com/EpicGames/UnrealEngine/blob/5.3/Engine/Source/Runtime/Engine/Private/GameplayStatics.cpp#L93
54 | */
55 | this.EngineVersion.Major = serial.readUInt16();
56 | this.EngineVersion.Minor = serial.readUInt16();
57 | this.EngineVersion.Patch = serial.readUInt16();
58 | this.EngineVersion.Build = serial.readUInt32();
59 | this.EngineVersion.BuildId = serial.readString();
60 |
61 | this.CustomFormatVersion = serial.readInt32();
62 | this.CustomFormatData.Count = serial.readInt32();
63 | for (let i = 0; i < this.CustomFormatData.Count; i++) {
64 | let guid = PropertyFactory.create({ Type: 'Guid' })
65 | this.CustomFormatData.Entries.push(guid.deserialize(serial));
66 | }
67 | this.SaveGameClassName = serial.readString();
68 | return this;
69 | }
70 | serialize() {
71 | let serial = Serializer.alloc(this.Size);
72 | serial.write(Buffer.from(this.Format));
73 | serial.writeInt32(this.SaveGameVersion);
74 | serial.writeInt32(this.PackageVersion);
75 | if (this.SaveGameVersion >= 3) {
76 | serial.writeInt32(this.PackageFileVersionUE5);
77 | } // UE 5 for F1M 23
78 |
79 | serial.writeUInt16(this.EngineVersion.Major);
80 | serial.writeUInt16(this.EngineVersion.Minor);
81 | serial.writeUInt16(this.EngineVersion.Patch);
82 | serial.writeUInt32(this.EngineVersion.Build);
83 | serial.writeString(this.EngineVersion.BuildId);
84 |
85 | serial.writeInt32(this.CustomFormatVersion);
86 | serial.writeInt32(this.CustomFormatData.Count);
87 | this.CustomFormatData.Entries.forEach(guid => serial.write(guid.serialize()));
88 | serial.writeString(this.SaveGameClassName);
89 | if (serial.tell != this.Size)
90 | throw new SerializationError(this);
91 | return serial.Data;
92 | }
93 | static from(obj) {
94 | let header = new GvasHeader();
95 | header.SaveGameVersion = obj.SaveGameVersion;
96 | header.PackageVersion = obj.PackageVersion;
97 | header.EngineVersion = obj.EngineVersion;
98 | header.CustomFormatVersion = obj.CustomFormatVersion;
99 | header.CustomFormatData.Count = obj.CustomFormatData.Count;
100 | obj.CustomFormatData.Entries.forEach(guid => {
101 | header.CustomFormatData.Entries.push(PropertyFactory.create(guid));
102 | });
103 | header.SaveGameClassName = obj.SaveGameClassName;
104 | return header;
105 | }
106 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright © 2021 by Ch1pset
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/PropertyErrors.js:
--------------------------------------------------------------------------------
1 | export class SerializationError extends Error {
2 | constructor(prop) {
3 | super(`Problem occurred during serialization of Property: ${prop.Name}`);
4 | }
5 | }
6 |
7 | export class DeserializationError extends Error {
8 | constructor(type, offset) {
9 | super(`Problem occurred during deserialization of Property '${type}' at offset 0x${offset.toString(16)}`)
10 | }
11 | }
12 |
13 | export class TypeNotImplementedError extends Error {
14 | constructor(type) {
15 | super(`No implementation for Property type: '${type}'`);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/Serializer.js:
--------------------------------------------------------------------------------
1 |
2 | import { Buffer } from "buffer";
3 | export class Serializer {
4 | constructor(buf) {
5 | this._data = buf;
6 | this._offset = 0;
7 | }
8 | get Data() { return this._data }
9 | get tell() { return this._offset }
10 | seek(count) {
11 | if(this._offset >= this._data.length)
12 | throw new Error(`Reached end of Buffer at offset 0x${this.tell.toString(16)}`);
13 | return this._offset += count;
14 | }
15 | read(count) {
16 | return this.Data.slice(this.tell, this.seek(count));
17 | }
18 | readInt32() {
19 | let int = this.Data.readInt32LE(this.tell);
20 | this.seek(4);
21 | return int;
22 | }
23 | readUInt32() {
24 | let int = this.Data.readUInt32LE(this.tell);
25 | this.seek(4);
26 | return int;
27 | }
28 | readInt64() {
29 | let int1 = this.Data.readUInt32LE(this.tell);
30 | this.seek(4);
31 | let int2 = this.Data.readInt32LE(this.tell);
32 | this.seek(4);
33 | const val = (BigInt(int2) << 32n) + BigInt(int1);
34 | if (val > (1n << 52n)) {
35 | return val.toString()
36 | }
37 | return Number(val);
38 | }
39 | readInt16() {
40 | let int = this.Data.readInt16LE(this.tell);
41 | this.seek(2)
42 | return int;
43 | }
44 | readUInt16() {
45 | let int = this.Data.readUInt16LE(this.tell);
46 | this.seek(2);
47 | return int;
48 | }
49 | readInt8() {
50 | let int = this.Data.readInt8(this.tell);
51 | this.seek(1);
52 | return int;
53 | }
54 | readUInt8() {
55 | let int = this.Data.readUInt8(this.tell);
56 | this.seek(1);
57 | return int;
58 | }
59 | readFloat() {
60 | let float = this.Data.readFloatLE(this.tell);
61 | this.seek(4);
62 | return float;
63 | }
64 | readString() {
65 | let length = this.readInt32();
66 | let str = this.read(length - 1).toString('latin1');
67 | this.read(1);
68 | return str;
69 | }
70 | readUnicodeString() {
71 | let length = this.readInt32();
72 | if (length < 0) {
73 | let str = this.read(-length * 2 - 2).toString('utf16le');
74 | this.read(2);
75 | return [str, "utf16le"];
76 | } else {
77 | let str = this.read(length - 1).toString('latin1');
78 | this.read(1);
79 | return [str, "latin1"];
80 | }
81 | }
82 | write(buf) {
83 | this._offset += buf.copy(this.Data, this.tell);
84 | }
85 | writeInt64(num) {
86 | let bi = BigInt.asIntN(64, BigInt(num));
87 | let high = Number(bi >> 32n);
88 | let low = Number(bi & ((1n << 32n) - 1n));
89 | this._offset = this.Data.writeUInt32LE(low, this.tell);
90 | this._offset = this.Data.writeInt32LE(high, this.tell);
91 | // TODO: this._offset = this.Data.writeBigInt64LE(num, this.tell);
92 | }
93 | writeUInt32(num) {
94 | this._offset = this.Data.writeUInt32LE(num, this.tell);
95 | }
96 | writeInt32(num) {
97 | this._offset = this.Data.writeInt32LE(num, this.tell);
98 | }
99 | writeUInt16(num) {
100 | this._offset = this.Data.writeUInt16LE(num, this.tell);
101 | }
102 | writeInt16(num) {
103 | this._offset = this.Data.writeInt16LE(num, this.tell);
104 | }
105 | writeUInt8(byte) {
106 | this._offset = this.Data.writeUInt8(byte, this.tell);
107 | }
108 | writeInt8(byte) {
109 | this._offset = this.Data.writeInt8(byte, this.tell);
110 | }
111 | writeFloat(num) {
112 | this._offset = this.Data.writeFloatLE(num, this.tell);
113 | }
114 | writeString(str) {
115 | this._offset = this.Data.writeInt32LE(str.length + 1, this.tell);
116 | this._offset += this.Data.write(str, this.tell);
117 | this._offset = this.Data.writeInt8(0, this.tell);
118 | }
119 | writeUTF16String(str) {
120 | this._offset += this.Data.write(str + "\0", this.tell, "utf16le");
121 | }
122 | writeLatin1String(str) {
123 | this._offset += this.Data.write(str + "\0", this.tell, "latin1");
124 | }
125 | append(buf) {
126 | this._data = Buffer.concat([this.Data, buf]);
127 | this._offset += buf.length;
128 | }
129 | static alloc(size) {
130 | return new Serializer(Buffer.alloc(size));
131 | }
132 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/arrays/IntArray.js:
--------------------------------------------------------------------------------
1 | import { Property } from '../properties/'
2 | import { PropertyFactory } from '../factories'
3 | import { SerializationError } from '..';
4 | import { Serializer } from '../Serializer';
5 |
6 | export class IntArray extends Property {
7 | constructor() {
8 | super();
9 | this.Type = "IntProperty"
10 | this.Properties = [];
11 | }
12 | get Size() {
13 | let size = this.Properties.length * 4;
14 | this.Properties.forEach((int) => {
15 | size += int.Size
16 | });
17 | return size;
18 | }
19 | get Count() {
20 | return this.Properties.length;
21 | }
22 | deserialize(serial, count) {
23 | serial.seek(count * 4);
24 | for (let i = 1; i < count; i++) {
25 | let Name = serial.readString();
26 | let Type = serial.readString();
27 | let Size = serial.readInt32();
28 | let prop = PropertyFactory.create({ Name, Type });
29 | prop.deserialize(serial);
30 | this.Properties.push(prop);
31 | }
32 | return this;
33 | }
34 | serialize() {
35 | let serial = Serializer.alloc(this.Size);
36 | serial.seek(this.Count * 4);
37 | this.Properties.forEach(int => serial.write(int.serialize()))
38 | if (serial.tell !== this.Size)
39 | throw new SerializationError(this);
40 | return serial.Data;
41 | }
42 | static from(obj) {
43 | let array = new IntArray();
44 | if (obj.Properties !== undefined)
45 | obj.Properties.forEach(int => array.Properties.push(PropertyFactory.create(int)));
46 | return array;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/arrays/SoftObjectArray.js:
--------------------------------------------------------------------------------
1 | import { Property } from '../properties/'
2 | import { SerializationError } from '..';
3 | import { Serializer } from '../Serializer';
4 |
5 | export class SoftObjectArray extends Property {
6 | constructor() {
7 | super();
8 | this.Type = "SoftObjectProperty";
9 | this.Properties = [];
10 | }
11 | get Size() {
12 | let size = 0;
13 | this.Properties.forEach((str) => {
14 | size += str.length + 1 + 4;
15 | size += 4;
16 | });
17 | return size;
18 | }
19 | get Count() {
20 | return this.Properties.length;
21 | }
22 | deserialize(serial, count) {
23 | for (let i = 0; i < count; i++) {
24 | this.Properties.push(serial.readString());
25 | serial.seek(4);
26 | }
27 | return this;
28 | }
29 | serialize() {
30 | let serial = Serializer.alloc(this.Size);
31 | this.Properties.forEach(str => {
32 | serial.writeString(str);
33 | serial.seek(4);
34 | });
35 | if (serial.tell !== this.Size)
36 | throw new SerializationError(this);
37 | return serial.Data;
38 | }
39 | static from(obj) {
40 | let array = new SoftObjectArray();
41 | if (obj.Properties !== undefined)
42 | array.Properties = obj.Properties;
43 | return array;
44 | }
45 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/arrays/StructArray.js:
--------------------------------------------------------------------------------
1 | import { StructProperty } from "../properties/index.js";
2 | import { PropertyFactory } from "../index.js";
3 |
4 | export class StructArray extends StructProperty {
5 | deserialize(serial, count) {
6 | // console.log(`Deserializing ${this.Name} Count: ${count}`)
7 | this.Name = serial.readString()
8 | this.Type = serial.readString()
9 | let Size = serial.readInt32();
10 | serial.seek(4);
11 | this.StoredPropertyType = serial.readString();
12 | serial.seek(17);
13 | let i = 0;
14 | while (i < count) {
15 | let Name = this.StoredPropertyType;
16 | let Type = 'Tuple';
17 | let prop = PropertyFactory.create({ Name, Type })
18 | prop.deserialize(serial)
19 | this.Properties.push(prop);
20 | i++;
21 | }
22 | // console.log(`Done Deserializing ${this.Name} Offset: ${serial.tell}`)
23 | return this;
24 | }
25 | static from(obj) {
26 | let struct = new StructArray();
27 | struct.Name = obj.Name;
28 | struct.Type = obj.Type;
29 | struct.StoredPropertyType = obj.StoredPropertyType;
30 | struct.Properties = [];
31 | if (obj.Properties !== undefined)
32 | obj.Properties.forEach((prop) => struct.Properties.push(PropertyFactory.create(prop)));
33 | return struct;
34 | }
35 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/arrays/index.js:
--------------------------------------------------------------------------------
1 |
2 | export { IntArray } from './IntArray'
3 | export { SoftObjectArray } from './SoftObjectArray'
4 | export { StructArray } from '../arrays/StructArray'
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/factories.js:
--------------------------------------------------------------------------------
1 | import { TypeNotImplementedError } from './index';
2 |
3 | class Factory {
4 | constructor() {
5 | this.Properties = {}
6 | this.Arrays = {}
7 | }
8 | create(obj) {
9 | let type = obj.Type
10 |
11 | if (this.Properties[type] === undefined)
12 | throw new TypeNotImplementedError(type);
13 |
14 | return this.Properties[type].from(obj);
15 | }
16 | createArray(obj) {
17 | let type = obj.Type
18 |
19 | if (this.Arrays[type] === undefined)
20 | throw new TypeNotImplementedError(type);
21 |
22 | return this.Arrays[type].from(obj);
23 | }
24 | }
25 |
26 | export const PropertyFactory = new Factory();
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/index.js:
--------------------------------------------------------------------------------
1 | import {IntArray, SoftObjectArray, StructArray} from './arrays'
2 | import {PropertyFactory} from './factories';
3 | import {
4 | ArrayProperty,
5 | BoolProperty,
6 | EnumProperty,
7 | FloatProperty,
8 | Guid,
9 | Int16Property,
10 | Int64Property,
11 | Int8Property,
12 | IntProperty,
13 | UInt32Property,
14 | ObjectProperty,
15 | SoftObjectProperty,
16 | StrProperty,
17 | StructProperty,
18 | Tuple
19 | } from './properties'
20 |
21 | PropertyFactory.Properties['ArrayProperty'] = ArrayProperty;
22 | PropertyFactory.Properties['BoolProperty'] = BoolProperty;
23 | PropertyFactory.Properties['EnumProperty'] = EnumProperty;
24 | PropertyFactory.Properties['FloatProperty'] = FloatProperty;
25 | PropertyFactory.Properties['IntProperty'] = IntProperty;
26 | PropertyFactory.Properties['UInt32Property'] = UInt32Property;
27 | PropertyFactory.Properties['Int64Property'] = Int64Property;
28 | PropertyFactory.Properties['UInt64Property'] = Int64Property;
29 | PropertyFactory.Properties['Int8Property'] = Int8Property;
30 | PropertyFactory.Properties['Int16Property'] = Int16Property;
31 | PropertyFactory.Properties['ObjectProperty'] = ObjectProperty;
32 | PropertyFactory.Properties['SoftObjectProperty'] = SoftObjectProperty;
33 | PropertyFactory.Properties['StrProperty'] = StrProperty;
34 | PropertyFactory.Properties['StructProperty'] = StructProperty;
35 | PropertyFactory.Properties['Tuple'] = Tuple;
36 | PropertyFactory.Properties['Guid'] = Guid;
37 | PropertyFactory.Arrays['IntArray'] = IntArray;
38 | PropertyFactory.Arrays['SoftObjectArray'] = SoftObjectArray;
39 | PropertyFactory.Arrays['StructProperty'] = StructArray;
40 | PropertyFactory.Arrays['IntProperty'] = IntArray;
41 | PropertyFactory.Arrays['IntProperty'] = IntArray;
42 | PropertyFactory.Arrays['SoftObjectProperty'] = SoftObjectArray;
43 |
44 | export { PropertyFactory }
45 | export { Gvas } from './Gvas'
46 | export { GvasHeader } from './GvasHeader'
47 | export { Serializer } from './Serializer'
48 | export * from './PropertyErrors'
49 | export * from './properties'
50 | export * from './arrays'
51 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/ArrayProperty.js:
--------------------------------------------------------------------------------
1 | import { Buffer } from 'buffer'
2 | import { Property } from './'
3 | import { PropertyFactory } from '../factories';
4 | import { SerializationError } from '../'
5 | import { Serializer } from '../Serializer';
6 |
7 | export class ArrayProperty extends Property {
8 | constructor() {
9 | super();
10 | this.StoredPropertyType = "";
11 | this.Property = new Property();
12 | }
13 | get Size() {
14 | let size = 0;
15 | size += this.Name.length + 1 + 4;
16 | size += this.Type.length + 1 + 4;
17 | size += 8; // 4 byte size + 4 byte padding
18 | size += this.StoredPropertyType.length + 1 + 4;
19 | size += 5; // 1 byte padding + 2 byte int + 2 byte padding
20 | size += this.Property.Size;
21 | return size;
22 | }
23 | get HeaderSize() {
24 | let size = this.Name.length + 1 + 4;
25 | size += this.Type.length + 1 + 4;
26 | size += 8;
27 | size += this.StoredPropertyType.length + 1 + 4;
28 | size += 1;
29 | return size;
30 | }
31 | get ArraySize() {
32 | if (this.StoredPropertyType === 'IntProperty')
33 | return 12;
34 | else
35 | return this.Size - this.HeaderSize;
36 | }
37 | deserialize(serial) {
38 | serial.seek(4);
39 | this.StoredPropertyType = serial.readString()
40 | serial.seek(1);
41 | let count = serial.readInt16();
42 | serial.seek(2);
43 | this.Property = PropertyFactory.createArray({
44 | Name: this.Name,
45 | Type: this.StoredPropertyType
46 | });
47 | this.Property.deserialize(serial, count)
48 |
49 | return this;
50 | }
51 | serialize() {
52 | let serial = Serializer.alloc(this.Size);
53 | serial.writeString(this.Name);
54 | serial.writeString(this.Type);
55 | serial.writeInt32(this.ArraySize);
56 | serial.seek(4);
57 | serial.writeString(this.StoredPropertyType);
58 | serial.seek(1);
59 | serial.writeInt16(this.Property.Count);
60 | serial.seek(2);
61 | serial.write(this.Property.serialize());
62 | if (serial.tell !== this.Size)
63 | throw new SerializationError(this);
64 | return serial.Data;
65 | }
66 | static from(obj) {
67 | let array = new ArrayProperty();
68 | array.Name = obj.Name;
69 | array.Type = obj.Type;
70 | array.StoredPropertyType = obj.StoredPropertyType;
71 | if (obj.Property !== undefined)
72 | array.Property = PropertyFactory.createArray(obj.Property);
73 | return array;
74 | }
75 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/BoolProperty.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { Serializer } from '..';
3 | import { SerializationError } from '../PropertyErrors';
4 |
5 | export class BoolProperty extends Property {
6 | constructor() {
7 | super();
8 | this.Property = false;
9 | }
10 | get Size() {
11 | return this.Name.length + 1 + 4
12 | + this.Type.length + 1 + 4
13 | + 10;
14 | }
15 | deserialize(serial) {
16 | serial.seek(4);
17 | this.Property = serial.readUInt8() === 1;
18 | serial.seek(1);
19 | return this;
20 | }
21 | serialize() {
22 | let serial = Serializer.alloc(this.Size);
23 | serial.writeString(this.Name);
24 | serial.writeString(this.Type);
25 | serial.seek(8);
26 | serial.writeUInt8(this.Property === true ? 1 : 0);
27 | serial.seek(1);
28 | if (serial.tell !== this.Size)
29 | throw new SerializationError(this)
30 | return serial.Data;
31 | }
32 | static from(obj) {
33 | let prop = new BoolProperty();
34 | obj.Property = !!obj.Property;
35 | Object.assign(prop, obj);
36 | return prop;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/EnumProperty.js:
--------------------------------------------------------------------------------
1 | import { Serializer } from '../Serializer';
2 | import { SerializationError } from '../PropertyErrors';
3 | import { Property } from '.';
4 |
5 | export class EnumProperty extends Property {
6 | constructor() {
7 | super();
8 | this.EnumType = "";
9 | this.Property = "";
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + this.Property.length + 1 + 4
15 | + this.EnumType.length + 1 + 4
16 | + 9;
17 | }
18 | deserialize(serial) {
19 | serial.seek(4);
20 | this.EnumType = serial.readString();
21 | serial.seek(1);
22 | this.Property = serial.readString();
23 | return this;
24 | }
25 | serialize() {
26 | let serial = Serializer.alloc(this.Size);
27 | serial.writeString(this.Name);
28 | serial.writeString(this.Type);
29 | serial.writeInt32(this.Property.length + 1 + 4);
30 | serial.seek(4);
31 | serial.writeString(this.EnumType);
32 | serial.seek(1);
33 | serial.writeString(this.Property);
34 | if (serial.tell !== this.Size)
35 | throw new SerializationError(this);
36 | return serial.Data;
37 | }
38 | static from(obj) {
39 | let prop = new EnumProperty();
40 | Object.assign(prop, obj);
41 | return prop;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/FloatProperty.js:
--------------------------------------------------------------------------------
1 | import { Serializer } from '../Serializer';
2 | import { SerializationError } from '../PropertyErrors';
3 | import { Property } from './'
4 |
5 | export class FloatProperty extends Property {
6 | constructor() {
7 | super();
8 | this.Property = 0;
9 | this.Index = 0;
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + 13;
15 | }
16 | deserialize(serial) {
17 | this.Index = serial.readInt32();
18 | serial.seek(1);
19 | this.Property = serial.readFloat();
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(4);
27 | serial.writeInt32(this.Index);
28 | serial.seek(1);
29 | serial.writeFloat(this.Property);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new FloatProperty();
36 | obj.Property = Number(obj.Property);
37 | Object.assign(prop, obj);
38 | return prop;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Guid.js:
--------------------------------------------------------------------------------
1 | import { Buffer } from 'buffer'
2 | import { Property } from './'
3 | import { SerializationError } from '..';
4 | import { Serializer } from '../Serializer';
5 |
6 | export class Guid extends Property {
7 | constructor() {
8 | super();
9 | this.Type = 'Guid';
10 | this.Id = "00000000-00-00-00-000000000000";
11 | this.Value = 0;
12 | }
13 | get Size() {
14 | return 20;
15 | }
16 | deserialize(serial) {
17 | this.Id = `${serial.read(4).swap32().toString('hex')}`
18 | this.Id += `-${serial.read(2).swap16().toString('hex')}`
19 | this.Id += `-${serial.read(2).swap16().toString('hex')}`
20 | this.Id += `-${serial.read(2).toString('hex')}`
21 | this.Id += `-${serial.read(6).toString('hex')}`
22 | this.Value = serial.readInt32();
23 | return this;
24 | }
25 | serialize() {
26 | let guid = this.Id.split('-');
27 | let serial = Serializer.alloc(this.Size);
28 | serial.write(Buffer.from(guid[0], 'hex').swap32());
29 | serial.write(Buffer.from(guid[1], 'hex').swap16());
30 | serial.write(Buffer.from(guid[2], 'hex').swap16());
31 | serial.write(Buffer.from(guid[3], 'hex'));
32 | serial.write(Buffer.from(guid[4], 'hex'));
33 | serial.writeInt32(this.Value);
34 | if (serial.tell !== 20)
35 | throw new SerializationError(this);
36 | return serial.Data;
37 | }
38 | static from(obj) {
39 | let guid = new Guid();
40 | guid.Id = obj.Id;
41 | guid.Value = obj.Value;
42 | return guid;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Int16Property.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { Serializer } from '..';
3 | import { SerializationError } from '../PropertyErrors';
4 |
5 | export class Int16Property extends Property {
6 | constructor() {
7 | super();
8 | this.Property = 0;
9 | this.Index = 0;
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + 9 + 2;
15 | }
16 | deserialize(serial) {
17 | this.Index = serial.readInt32();
18 | serial.seek(1);
19 | this.Property = serial.readInt16();
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(4);
27 | serial.writeInt32(this.Index);
28 | serial.seek(1);
29 | serial.writeInt16(this.Property);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new Int16Property();
36 | obj.Property = obj.Property || 0;
37 | const lb = -1 << 15;
38 | const ub = (1 << 15) - 1;
39 | if (obj.Property > ub || obj.Property < lb) {
40 | throw Error(`${obj.Name} = ${obj.Property} out of range [${lb}, ${ub}]`)
41 | }
42 | Object.assign(prop, obj);
43 | return prop;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Int64Property.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { Serializer } from '..';
3 | import { SerializationError } from '../PropertyErrors';
4 |
5 | export class Int64Property extends Property {
6 | constructor() {
7 | super();
8 | this.Property = 0n;
9 | this.Index = 0;
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + 9 + 8;
15 | }
16 | deserialize(serial) {
17 | this.Index = serial.readInt32();
18 | serial.seek(1);
19 | this.Property = serial.readInt64();
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(4);
27 | serial.writeInt32(this.Index);
28 | serial.seek(1);
29 | serial.writeInt64(this.Property);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new Int64Property();
36 | let bi = 0n;
37 | try {
38 | bi = BigInt(obj.Property || 0);
39 | const lb = -1n << 64n;
40 | const ub = (1n << 63n) - 1n;
41 | if (bi > ub || bi < lb) {
42 | throw Error(`out of range [${lb}, ${ub}]`)
43 | }
44 | const doubleVal = Number(obj.Property);
45 | if (Math.abs(doubleVal) >= (2 ** 53)) {
46 | obj.Property = BigInt(obj.Property).toString();
47 | } else {
48 | obj.Property = Number(obj.Property);
49 | }
50 | } catch (e) {
51 | throw Error(`${obj.Name} = ${obj.Property}: ${e.toString()}`)
52 | }
53 | Object.assign(prop, obj);
54 | return prop;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Int8Property.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { Serializer } from '..';
3 | import { SerializationError } from '../PropertyErrors';
4 |
5 | export class Int8Property extends Property {
6 | constructor() {
7 | super();
8 | this.Property = 0;
9 | this.Index = 0;
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + 9 + 1;
15 | }
16 | deserialize(serial) {
17 | this.Index = serial.readInt32();
18 | serial.seek(1);
19 | this.Property = serial.readInt8();
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(4);
27 | serial.writeInt32(this.Index);
28 | serial.seek(1);
29 | serial.writeInt8(this.Property);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new Int8Property();
36 | obj.Property = obj.Property || 0;
37 | const lb = -128;
38 | const ub = 127;
39 | if (obj.Property > ub || obj.Property < lb) {
40 | throw Error(`${obj.Name} = ${obj.Property} out of range [${lb}, ${ub}]`)
41 | }
42 | Object.assign(prop, obj);
43 | return prop;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/IntProperty.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { Serializer } from '..';
3 | import { SerializationError } from '../PropertyErrors';
4 |
5 | export class IntProperty extends Property {
6 | constructor() {
7 | super();
8 | this.Property = 0;
9 | this.Index = 0;
10 | }
11 | get Size() {
12 | return this.Name.length + 1 + 4
13 | + this.Type.length + 1 + 4
14 | + 9 + 4;
15 | }
16 | deserialize(serial) {
17 | this.Index = serial.readInt32();
18 | serial.seek(1);
19 | this.Property = serial.readInt32();
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(4);
27 | serial.writeInt32(this.Index);
28 | serial.seek(1);
29 | serial.writeInt32(this.Property);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new IntProperty();
36 | obj.Property = obj.Property || 0;
37 | const lb = -1 << 31;
38 | const ub = 0x7fffffff;
39 | if (obj.Property > ub || obj.Property < lb) {
40 | throw Error(`${obj.Name} = ${obj.Property} out of range [${lb}, ${ub}]`)
41 | }
42 | Object.assign(prop, obj);
43 | return prop;
44 | }
45 | }
46 |
47 | export class UInt32Property extends Property {
48 | constructor() {
49 | super();
50 | this.Property = 0;
51 | this.Index = 0;
52 | }
53 | get Size() {
54 | return this.Name.length + 1 + 4
55 | + this.Type.length + 1 + 4
56 | + 9 + 4;
57 | }
58 | deserialize(serial) {
59 | this.Index = serial.readInt32();
60 | serial.seek(1);
61 | this.Property = serial.readUInt32();
62 | return this;
63 | }
64 | serialize() {
65 | let serial = Serializer.alloc(this.Size);
66 | serial.writeString(this.Name);
67 | serial.writeString(this.Type);
68 | serial.writeInt32(4);
69 | serial.writeInt32(this.Index);
70 | serial.seek(1);
71 | serial.writeUInt32(this.Property);
72 | if (serial.tell !== this.Size)
73 | throw new SerializationError(this);
74 | return serial.Data;
75 | }
76 | static from(obj) {
77 | let prop = new IntProperty();
78 | obj.Property = obj.Property || 0;
79 | const lb = 0;
80 | const ub = 0xffffffff;
81 | if (obj.Property > ub || obj.Property < lb) {
82 | throw Error(`${obj.Name} = ${obj.Property} out of range [${lb}, ${ub}]`)
83 | }
84 | Object.assign(prop, obj);
85 | return prop;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/ObjectProperty.js:
--------------------------------------------------------------------------------
1 | import { Serializer } from '../Serializer';
2 | import { SerializationError } from '../PropertyErrors';
3 | import { Property } from './'
4 |
5 | export class ObjectProperty extends Property {
6 | constructor() {
7 | super();
8 | this.Property = "";
9 | }
10 | get Size() {
11 | return this.Name.length + 1 + 4
12 | + this.Type.length + 1 + 4
13 | + this.Property.length + 1 + 4
14 | + 9;
15 | }
16 | deserialize(serial) {
17 | serial.seek(5);
18 | this.Property = serial.readString();
19 | return this;
20 | }
21 | serialize() {
22 | let serial = Serializer.alloc(this.Size);
23 | serial.writeString(this.Name);
24 | serial.writeString(this.Type);
25 | serial.writeInt32(this.Property.length + 1 + 4);
26 | serial.seek(5);
27 | serial.writeString(this.Property);
28 | if (serial.tell !== this.Size)
29 | throw new SerializationError(this);
30 | return serial.Data;
31 | }
32 | static from(obj) {
33 | let prop = new ObjectProperty();
34 | Object.assign(prop, obj);
35 | return prop;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Property.js:
--------------------------------------------------------------------------------
1 | import { Serializer } from '../'
2 | export class Property {
3 | constructor() {
4 | this.Name = "";
5 | this.Type = "";
6 | }
7 | /**
8 | * Per-property byte size getter
9 | * @returns {Number} `Size` in bytes of all attributes and properties held by this property to be serialized
10 | */
11 | get Size() {
12 | throw new Error(`Size getter not implemented for property: ${this.Type}`);
13 | }
14 | /**
15 | * Per-property deserialization function
16 | * @param {Serializer} serial Serializer instance used to read a buffer
17 | * @param {Number} size Size in bytes or Count of elements for Arrays
18 | * @returns {Property} Returns `this` instance
19 | */
20 | deserialize(serial, size) {
21 | throw new Error(`Deserialization not implemented for property: ${this.Type}`);
22 | }
23 | /**
24 | * Per-property serialization function
25 | * @returns {Buffer} Returns a `Buffer` of the serialized data
26 | */
27 | serialize() {
28 | throw new Error(`Serialization not implemented for property: ${this.Type}`);
29 | }
30 | /**
31 | * Factory function for a `Property` type. This should instantiate a `new Property` with default values if not given in the `json`
32 | * @param {Object} json Template from which to create a new instance of a `Property`
33 | */
34 | static from(json) {
35 | throw new Error(`from() not implemented for property: ${this.Type}`);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/SoftObjectProperty.js:
--------------------------------------------------------------------------------
1 | import { SerializationError } from '../PropertyErrors';
2 | import { Property } from './'
3 | import { Serializer } from '../Serializer';
4 |
5 | export class SoftObjectProperty extends Property {
6 | constructor() {
7 | super();
8 | this.Property = "";
9 | }
10 | get Size() {
11 | return this.Name.length + 1 + 4
12 | + this.Type.length + 1 + 4
13 | + this.Property.length + 1 + 4
14 | + 13;
15 | }
16 | deserialize(serial) {
17 | serial.seek(5);
18 | this.Property = serial.readString();
19 | serial.seek(4);
20 | return this;
21 | }
22 | serialize() {
23 | let serial = Serializer.alloc(this.Size);
24 | serial.writeString(this.Name);
25 | serial.writeString(this.Type);
26 | serial.writeInt32(this.Property.length + 8);
27 | serial.seek(5);
28 | serial.writeString(this.Property);
29 | serial.seek(4);
30 | if (serial.tell !== this.Size)
31 | throw new SerializationError(this);
32 | return serial.Data;
33 | }
34 | static from(obj) {
35 | let prop = new SoftObjectProperty();
36 | Object.assign(prop, obj);
37 | return prop;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/StrProperty.js:
--------------------------------------------------------------------------------
1 | import {SerializationError} from "../PropertyErrors";
2 | import {Serializer} from '../Serializer';
3 | import {Property} from './'
4 | import { Buffer } from "buffer";
5 |
6 | const is8Bit = string => /^[\x00-\xFF]*$/.test(string);
7 |
8 | export class StrProperty extends Property {
9 | constructor() {
10 | super();
11 | this.Property = "";
12 | }
13 |
14 | get Encoding() {
15 | return is8Bit(this.Property) ? "latin1" : "utf16le";
16 | }
17 |
18 | get StringEncodedLength() {
19 | return Buffer.from(this.Property + "\0", this.Encoding).length;
20 | }
21 |
22 | get Size() {
23 | const baseLength = this.Name.length + 1 + 4 + this.Type.length + 1 + 4;
24 | return baseLength + this.StringEncodedLength + 4 + 9;
25 | }
26 | deserialize(serial) {
27 | serial.seek(5);
28 | [this.Property, ] = serial.readUnicodeString();
29 | return this;
30 | }
31 | serialize() {
32 | let serial = Serializer.alloc(this.Size);
33 | serial.writeString(this.Name);
34 | serial.writeString(this.Type);
35 |
36 | serial.writeInt32(this.StringEncodedLength + 4);
37 | serial.seek(5);
38 | switch (this.Encoding) {
39 | case "latin1":
40 | serial.writeInt32(this.StringEncodedLength);
41 | serial.writeLatin1String(this.Property);
42 | break;
43 | case "utf16le":
44 | serial.writeInt32(-(this.StringEncodedLength / 2));
45 | serial.writeUTF16String(this.Property);
46 | break;
47 | }
48 | if (serial.tell !== this.Size)
49 | throw new SerializationError(this);
50 | return serial.Data;
51 | }
52 | static from(obj) {
53 | let prop = new StrProperty();
54 | Object.assign(prop, obj);
55 | return prop;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/StrProperty_.js:
--------------------------------------------------------------------------------
1 | import {SerializationError} from "../PropertyErrors";
2 | import {Serializer} from '../Serializer';
3 | import {Property} from './'
4 |
5 | const is8Bit = string => /^[\x00-\xFF]*$/.test(string);
6 |
7 | export class StrProperty extends Property {
8 | constructor() {
9 | super();
10 | this.Property = "";
11 | this.Encoding = "latin1";
12 | }
13 |
14 | get Encoding() {
15 | return is8Bit(this.Property) ? "latin1" : "utf16le";
16 | }
17 |
18 | get StringEncodedLength() {
19 | return Buffer.from(this.Property + "\0", this.Encoding).length;
20 | }
21 |
22 | get Size() {
23 | const baseLength = this.Name.length + 1 + 4 + this.Type.length + 1 + 4;
24 | return baseLength + this.StringEncodedLength + 4 + 9;
25 | }
26 | deserialize(serial) {
27 | serial.seek(5);
28 | [this.Property, this.Encoding] = serial.readUnicodeString();
29 | return this;
30 | }
31 | serialize() {
32 | let serial = Serializer.alloc(this.Size);
33 | serial.writeString(this.Name);
34 | serial.writeString(this.Type);
35 |
36 | serial.writeInt32(this.StringEncodedLength + 4);
37 | serial.seek(5);
38 | switch (this.Encoding) {
39 | case "latin1":
40 | serial.writeInt32(this.StringEncodedLength);
41 | serial.writeLatin1String(this.Property);
42 | break;
43 | case "utf16le":
44 | serial.writeInt32(-(this.StringEncodedLength / 2));
45 | serial.writeUTF16String(this.Property);
46 | break;
47 | }
48 | if (serial.tell !== this.Size)
49 | throw new SerializationError(this);
50 | return serial.Data;
51 | }
52 | static from(obj) {
53 | let prop = new StrProperty();
54 | if (obj.Encoding === "utf8") {
55 | obj.Encoding = "latin1";
56 | console.warn("utf8 should be latin1");
57 | }
58 |
59 | if (obj.Encoding === "latin1") {
60 | if (!is8Bit(obj.Property)) {
61 | throw Error(`${obj.Name} = ${obj.Property} is outside latin1. consider using utf16le?`)
62 | }
63 | } else if (obj.Encoding === "utf16le") {
64 | } else if (!obj.Encoding) {
65 | } else {
66 | throw Error(`${obj.Name}: ${obj.Encoding} is unsupported. valid options are: [latin1, utf16le]`)
67 | }
68 | Object.assign(prop, obj);
69 | return prop;
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/StructProperty.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { PropertyFactory } from '../factories';
3 | import { Serializer } from '../Serializer';
4 |
5 | export class StructProperty extends Property {
6 | constructor() {
7 | super();
8 | this.StoredPropertyType = "";
9 | this.Properties = [];
10 | }
11 | get Size() {
12 | let size = this.Name.length + 1 + 4;
13 | size += this.Type.length + 1 + 4;
14 | size += 8; // 4 byte size + 4 byte padding
15 | size += this.StoredPropertyType.length + 1 + 4;
16 | size += 17; // 17 byte padding
17 | for (let i = 0; i < this.Properties.length; i++) {
18 | size += this.Properties[i].Size;
19 | }
20 | return size;
21 | }
22 | get HeaderSize() {
23 | let size = this.Name.length + 1 + 4;
24 | size += this.Type.length + 1 + 4;
25 | size += 8;
26 | size += this.StoredPropertyType.length + 1 + 4;
27 | size += 17;
28 | return size
29 | }
30 | get Count() {
31 | return this.Properties.length;
32 | }
33 | deserialize(serial, size) {
34 | // console.log(`Deserializing ${this.Name} Size: ${size}`)
35 | serial.seek(4);
36 | this.StoredPropertyType = serial.readString();
37 | serial.seek(17);
38 | let end = serial.tell + size;
39 | let i = 0;
40 | while (serial.tell < end) {
41 | let Name = this.StoredPropertyType;
42 | let Type = 'Tuple';
43 | let prop = PropertyFactory.create({ Name, Type })
44 | prop.deserialize(serial)
45 | this.Properties.push(prop);
46 | i++;
47 | }
48 | // console.log(`Done Deserializing ${this.Name} Offset: ${serial.tell}`)
49 | return this;
50 | }
51 | serialize() {
52 | let serial = Serializer.alloc(this.Size);
53 | serial.writeString(this.Name);
54 | serial.writeString(this.Type);
55 | serial.writeInt32(this.Size - this.HeaderSize);
56 | serial.seek(4);
57 | serial.writeString(this.StoredPropertyType);
58 | serial.seek(17);
59 | for (let i = 0; i < this.Properties.length; i++) {
60 | serial.write(this.Properties[i].serialize());
61 | }
62 | if (serial.tell !== this.Size)
63 | throw new SerializationError(this);
64 | return serial.Data;
65 | }
66 | static from(obj) {
67 | let struct = new StructProperty();
68 | struct.Name = obj.Name;
69 | struct.Type = obj.Type;
70 | struct.StoredPropertyType = obj.StoredPropertyType;
71 | struct.Properties = [];
72 | if (obj.Properties !== undefined)
73 | obj.Properties.forEach((prop) => struct.Properties.push(PropertyFactory.create(prop)));
74 | return struct;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/Tuple.js:
--------------------------------------------------------------------------------
1 | import { Property } from './'
2 | import { PropertyFactory } from '../factories';
3 | import { SerializationError } from '..';
4 | import { Serializer } from '../Serializer';
5 |
6 | export class Tuple extends Property {
7 | constructor() {
8 | super();
9 | this.Type = 'Tuple';
10 | this.Properties = [];
11 | }
12 | get Size() {
13 | let size = 0;
14 | for (let i = 0; i < this.Properties.length; i++) {
15 | size += this.Properties[i].Size;
16 | }
17 | size += 9;
18 | return size;
19 | }
20 | get Count() {
21 | return this.Properties.length;
22 | }
23 | deserialize(serial) {
24 | let Name;
25 | while ((Name = serial.readString()) !== 'None') {
26 | let Type = serial.readString();
27 | let Size = serial.readInt32();
28 | let prop = PropertyFactory.create({ Name, Type });
29 | prop.deserialize(serial, Size);
30 | this.Properties.push(prop);
31 | }
32 | return this;
33 | }
34 | serialize() {
35 | let serial = Serializer.alloc(this.Size);
36 | for (let i = 0; i < this.Properties.length; i++) {
37 | serial.write(this.Properties[i].serialize());
38 | }
39 | serial.writeString('None');
40 | if (serial.tell !== this.Size)
41 | throw new SerializationError(this);
42 | return serial.Data;
43 | }
44 | static from(obj) {
45 | let tuple = new Tuple();
46 | tuple.Name = obj.Name;
47 | if (obj.Properties !== undefined)
48 | obj.Properties.forEach(prop => tuple.Properties.push(PropertyFactory.create(prop)));
49 | return tuple;
50 | }
51 | }
--------------------------------------------------------------------------------
/src/js/backend/UESaveTool/properties/index.js:
--------------------------------------------------------------------------------
1 | export { Property } from './Property'
2 | export { BoolProperty } from './BoolProperty'
3 | export { IntProperty } from './IntProperty'
4 | export { Int8Property } from './Int8Property'
5 | export { Int16Property } from './Int16Property'
6 | export { UInt32Property } from './IntProperty'
7 | export { Int64Property } from './Int64Property'
8 | export { FloatProperty } from './FloatProperty'
9 | export { StrProperty } from './StrProperty'
10 | export { ObjectProperty } from './ObjectProperty'
11 | export { SoftObjectProperty } from './SoftObjectProperty'
12 | export { StructProperty } from './StructProperty'
13 | export { ArrayProperty } from './ArrayProperty'
14 | export { EnumProperty } from './EnumProperty'
15 | export { Tuple } from './Tuple'
16 | export { Guid } from './Guid'
17 |
--------------------------------------------------------------------------------
/src/js/backend/command.js:
--------------------------------------------------------------------------------
1 | // Command.js
2 | import { updateFront } from "../frontend/renderer";
3 | import { dbWorker } from "../frontend/dragFile";
4 | import { teamReplaceDict, prettyNames, getGlobals, setGlobals } from "./commandGlobals";
5 |
6 |
7 | export class Command {
8 | /**
9 | * @param {string} commandName - Name of the command to execute.
10 | * @param {Object} data - Data to send to the worker.
11 | */
12 | constructor(commandName, data) {
13 | this.commandName = commandName;
14 | this.data = data;
15 | }
16 |
17 |
18 | async execute() {
19 | console.log(`[Command] Executing command: ${this.commandName}`);
20 | console.log(`[Command] Data:`, this.data);
21 |
22 | dbWorker.postMessage({ command: this.commandName, data: this.data });
23 |
24 | dbWorker.onmessage = (msg) => {
25 | const response = msg.data;
26 | if (response.error) {
27 | console.error(`[${this.commandName}] Error:`, response.error);
28 | document.querySelector(".error").classList.remove("d-none");
29 | } else {
30 | console.log(`[${this.commandName}] Response:`, response.responseMessage);
31 | updateFront(response);
32 | if (this.commandName === "saveSelected") {
33 | if (response.responseMessage === "Game Year") {
34 | this.updateTeamsFor24(response.content[0]);
35 | this.addTeam("Custom Team", response.content[1]);
36 | }
37 | }
38 | }
39 |
40 | };
41 | }
42 |
43 | promiseExecute() {
44 | return new Promise((resolve, reject) => {
45 | const handler = (e) => {
46 | const resp = e.data;
47 | if (resp.command && resp.command !== this.commandName) return;
48 | dbWorker.removeEventListener("message", handler);
49 |
50 | if (resp.error) return reject(resp.error);
51 | resolve(resp);
52 | };
53 |
54 | dbWorker.addEventListener("message", handler);
55 | dbWorker.postMessage({ command: this.commandName, data: this.data });
56 | });
57 | }
58 |
59 | updateTeamsFor24(year) {
60 | if (year === "24") {
61 | const data = {
62 | teams: {
63 | alphatauri: "visarb",
64 | alpine: "alpine",
65 | alfa: "stake"
66 | }
67 | }
68 | this.replaceTeam("Alpha Tauri", data.teams.alphatauri);
69 | this.replaceTeam("Alpine", data.teams.alpine);
70 | this.replaceTeam("Alfa Romeo", data.teams.alfa);
71 | const yearResponse = { responseMessage: "24 Year", content: data };
72 | updateFront(yearResponse);
73 | }
74 | }
75 |
76 | replaceTeam(originalTeam, newTeam) {
77 | teamReplaceDict[originalTeam] = prettyNames[newTeam] || newTeam;
78 | }
79 |
80 | addTeam(originalTeam, newTeam) {
81 | teamReplaceDict[originalTeam] = newTeam;
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/js/backend/commandGlobals.js:
--------------------------------------------------------------------------------
1 | // commandGlobals.js
2 | export const teamReplaceDict = {
3 | "Alpha Tauri": "Alpha Tauri", "Alpine": "Alpine", "Alfa Romeo": "Alfa Romeo", "Aston Martin": "Aston Martin",
4 | "Ferrari": "Ferrari", "Haas": "Haas", "McLaren": "McLaren", "Mercedes": "Mercedes",
5 | "Red Bull": "Red Bull", "Williams": "Williams", "Renault": "Renault", "F2": "Formula 2", "F3": "Formula 3", "Custom Team": "Custom Team"
6 | };
7 |
8 | export const prettyNames = {
9 | "visarb": "Visa Cashapp RB", "toyota": "Toyota", "hugo": "Hugo Boss", "alphatauri": "Alpha Tauri", "brawn": "Brawn GP", "porsche": "Porsche",
10 | "alpine": "Alpine", "renault": "Renault", "andretti": "Andretti", "lotus": "Lotus", "alfa": "Alfa Romeo",
11 | "audi": "Audi", "sauber": "Sauber", "stake": "Stake Sauber"
12 | };
13 |
14 | let path = null;
15 | let yearIteration = null;
16 | let isCreateATeam = false;
17 | let currentDate = null;
18 |
19 | export function setGlobals({dbPath, year, createTeam, date }) {
20 | path = dbPath || path;
21 | yearIteration = year || yearIteration;
22 | isCreateATeam = createTeam || isCreateATeam;
23 | currentDate = date || currentDate;
24 | }
25 |
26 | export function getGlobals() {
27 | return { path, yearIteration, isCreateATeam, currentDate };
28 | }
29 |
--------------------------------------------------------------------------------
/src/js/backend/dbManager.js:
--------------------------------------------------------------------------------
1 | let db = null;
2 | let metadata = null;
3 |
4 | export function setDatabase(database, meta) {
5 | db = database;
6 |
7 | metadata = meta;
8 | }
9 |
10 | export function getDatabase() {
11 | return db;
12 | }
13 |
14 | export function getMetadata() {
15 | return metadata;
16 | }
17 |
18 | export function setMetaData(meta) {
19 | metadata = meta;
20 | }
21 |
22 | /**
23 | * Ejecuta una consulta SQL y devuelve el resultado según 'type'.
24 | * @param {string} query - La consulta a ejecutar.
25 | * @param {"singleValue"|"singleRow"|"allRows"} [type="allRows"] - El tipo de resultado.
26 | * @returns {any}
27 | * - 'singleValue': un único valor (o null).
28 | * - 'singleRow': la primera fila (array de valores) o null.
29 | * - 'allRows': array de filas (cada fila, array de valores), o [] si no hay ninguna.
30 | */
31 | export function queryDB(query, type = 'allRows') {
32 | const res = db.exec(query); // o tu instancia real de db
33 | if (!res.length) {
34 | // No hay resultsets
35 | return (type === 'allRows') ? [] : null;
36 | }
37 |
38 | const rows = res[0].values;
39 | if (!rows.length) {
40 | // Hay resultset pero 0 filas
41 | return (type === 'allRows') ? [] : null;
42 | }
43 |
44 | switch (type) {
45 | case 'singleValue':
46 | // Devuelvo la primera columna de la primera fila
47 | return rows[0][0] ?? null;
48 | case 'singleRow':
49 | // Devuelvo la primera fila entera (array)
50 | return rows[0];
51 | case 'allRows':
52 | default:
53 | // Devuelvo todas las filas
54 | return rows;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/js/backend/scriptUtils/calendarUtils.js:
--------------------------------------------------------------------------------
1 | import { queryDB } from "../dbManager";
2 |
3 | const weatherDict = {
4 | "0": 1,
5 | "1": 2,
6 | "2": 4,
7 | "3": 8,
8 | "4": 16,
9 | "5": 32
10 | };
11 |
12 | export function editCalendar(calendarStr, year_iteration, racesData) {
13 | const calendar = calendarStr.toLowerCase();
14 | const yearIteration = year_iteration;
15 |
16 | let maxRaces;
17 | let weeks;
18 |
19 | if (yearIteration === "24") {
20 | maxRaces = 24;
21 | weeks = [11, 8, 15, 36, 24, 20, 22, 25, 26, 9, 28, 29, 34, 37, 13, 42, 41, 43, 48, 17, 33, 19, 46, 47];
22 | } else if (yearIteration === "23") {
23 | maxRaces = 23;
24 | weeks = [12, 8, 16, 21, 20, 23, 25, 26, 10, 28, 29, 34, 36, 37, 42, 41, 43, 46, 17, 33, 19, 45, 39];
25 | } else {
26 | maxRaces = 0;
27 | weeks = [];
28 | }
29 |
30 | const raceBlanks = maxRaces - racesData.length;
31 |
32 | const daySeason = queryDB(`
33 | SELECT Day, CurrentSeason
34 | FROM Player_State
35 | `, 'singleRow');
36 |
37 | let actualCalendar = queryDB(`
38 | SELECT TrackID
39 | FROM Races
40 | WHERE SeasonID = ${daySeason[1]}
41 | `, 'allRows') || [];
42 |
43 | actualCalendar = actualCalendar.map(row => row[0]);
44 | //build newCalendar with trackId from each element from the array racesData
45 | const newCalendar = racesData.map(race => parseInt(race.trackId));
46 |
47 | if (arraysEqual(actualCalendar, newCalendar)) {
48 | const ids = queryDB(`
49 | SELECT RaceID
50 | FROM Races
51 | WHERE SeasonID = ${daySeason[1]}
52 | `, 'allRows') || [];
53 | const raceIDs = ids.map(row => row[0]);
54 |
55 | for (let i = 0; i < racesData.length; i++) {
56 | const race = racesData[i];
57 | const state = race.state;
58 | const format = race.type;
59 | const rainR = weatherDict[race.rainRace];
60 | const rainRBool = (parseFloat(rainR) >= 8) ? 1 : 0;
61 | const rainQ = weatherDict[race.rainQuali];
62 | const rainQBool = (parseFloat(rainQ) >= 8) ? 1 : 0;
63 | const rainP = weatherDict[race.rainPractice];
64 | const rainPBool = (parseFloat(rainP) >= 8) ? 1 : 0;
65 | // race_code = race.slice(0, -5); // en Python, no lo usas aquí para nada
66 |
67 | queryDB(`
68 | UPDATE Races
69 | SET
70 | RainPractice = ${rainPBool},
71 | WeatherStatePractice = ${rainP},
72 | RainQualifying = ${rainQBool},
73 | WeatherStateQualifying = ${rainQ},
74 | RainRace = ${rainRBool},
75 | WeatherStateRace = ${rainR},
76 | WeekendType = ${format}
77 | WHERE RaceID = ${raceIDs[i]}
78 | `);
79 | }
80 | } else {
81 | const randomBlanks = [];
82 | for (let i = 0; i < raceBlanks; i++) {
83 | let n = Math.floor(Math.random() * maxRaces);
84 | while (randomBlanks.includes(n)) {
85 | n = Math.floor(Math.random() * maxRaces);
86 | }
87 | randomBlanks.push(n);
88 | }
89 |
90 | for (const el of randomBlanks) {
91 | weeks[el] = 0;
92 | }
93 |
94 | weeks = weeks.filter(x => x !== 0);
95 | weeks.sort((a, b) => a - b);
96 |
97 |
98 |
99 | let leapYearCount = 2;
100 | const yearDiff = daySeason[1] - 2023;
101 | leapYearCount += yearDiff;
102 |
103 | let dayStart = 44927 + (yearDiff * 365) + Math.floor(leapYearCount / 4);
104 | const dayOfWeek = dayStart % 7;
105 | const daysUntilSunday = (8 - dayOfWeek) % 7;
106 | dayStart += daysUntilSunday;
107 |
108 | const lastRaceLastSeason = queryDB(`
109 | SELECT MAX(RaceID)
110 | FROM Races
111 | WHERE SeasonID = ${daySeason[1] - 1}
112 | `, 'singleValue');
113 |
114 | const firstRaceThisSeason = queryDB(`
115 | SELECT MIN(RaceID)
116 | FROM Races
117 | WHERE SeasonID = ${daySeason[1]}
118 | `, 'singleValue');
119 |
120 | let raceid;
121 | if (parseInt(lastRaceLastSeason, 10) === (parseInt(firstRaceThisSeason, 10) - 1)) {
122 | raceid = lastRaceLastSeason;
123 | } else {
124 | raceid = firstRaceThisSeason - 1;
125 | }
126 |
127 | queryDB(`
128 | DELETE FROM Races
129 | WHERE State != 2
130 | AND SeasonID = ${daySeason[1]}
131 | `);
132 |
133 | for (let i = 0; i < racesData.length; i++) {
134 | const race = racesData[i];
135 | const state = race.state;
136 | const format = race.type;
137 | const rainR = weatherDict[race.rainRace];
138 | const rainRBool = (parseFloat(rainR) >= 8) ? 1 : 0;
139 | const rainQ = weatherDict[race.rainQuali];
140 | const rainQBool = (parseFloat(rainQ) >= 8) ? 1 : 0;
141 | const rainP = weatherDict[race.rainPractice];
142 | const rainPBool = (parseFloat(rainP) >= 8) ? 1 : 0;
143 | const raceCode = parseInt(race.trackId);
144 |
145 | const temps = queryDB(`
146 | SELECT TemperatureMin, TemperatureMax
147 | FROM Races_Templates
148 | WHERE TrackID = ${raceCode}
149 | `, 'singleRow');
150 |
151 | const tempP = randomInt(temps[0], temps[1]);
152 | const tempQ = randomInt(temps[0], temps[1]);
153 | const tempR = randomInt(temps[0], temps[1]);
154 |
155 | const day = ((weeks[i] + 1) * 7) + dayStart;
156 | raceid += 1;
157 |
158 | if (state !== "2") {
159 | queryDB(`
160 | INSERT INTO Races
161 | VALUES (
162 | ${raceid},
163 | ${daySeason[1]},
164 | ${day},
165 | ${raceCode},
166 | ${state},
167 | ${rainPBool},
168 | ${tempP},
169 | ${rainP},
170 | ${rainQBool},
171 | ${tempQ},
172 | ${rainQ},
173 | ${rainRBool},
174 | ${tempR},
175 | ${rainR},
176 | ${format}
177 | )
178 | `);
179 | }
180 | }
181 | }
182 | }
183 |
184 | // Helpers
185 |
186 | function arraysEqual(a, b) {
187 | if (a.length !== b.length) return false;
188 | for (let i = 0; i < a.length; i++) {
189 | if (a[i] !== b[i]) return false;
190 | }
191 | return true;
192 | }
193 |
194 | function randomInt(min, max) {
195 | const mn = parseInt(min, 10);
196 | const mx = parseInt(max, 10);
197 | return Math.floor(Math.random() * (mx - mn + 1)) + mn;
198 | }
199 |
--------------------------------------------------------------------------------
/src/js/backend/scriptUtils/carConstants.js:
--------------------------------------------------------------------------------
1 | export const stats = {
2 | 0: "airflow_front",
3 | 1: "airflow_sensitivity",
4 | 2: "brake_cooling",
5 | 3: "drs_delta",
6 | 4: "drag_reduction",
7 | 5: "engine_cooling",
8 | 6: "fuel_efficiency",
9 | 7: "low_speed_downforce",
10 | 8: "medium_speed_downforce",
11 | 9: "high_speed_downforce",
12 | 10: "power",
13 | 11: "performance_loss",
14 | 12: "performance_threshold",
15 | 13: "airflow_middle",
16 | 14: "operational_range",
17 | 15: "lifespan",
18 | 16: "special_weight"
19 | };
20 |
21 | export const defaultPartsStats = {
22 | 3: [3, 4, 5, 13, 15],
23 | 4: [0, 1, 2, 7, 8, 9, 15],
24 | 5: [1, 3, 4, 7, 8, 9, 15],
25 | 6: [0, 4, 5, 13, 15],
26 | 7: [1, 4, 7, 8, 9, 15],
27 | 8: [0, 2, 4, 7, 8, 9, 15]
28 | };
29 |
30 | export const unitValueToValue = {
31 | 0: (x) => x * 10,
32 | 1: (x) => x * 10,
33 | 2: (x) => x * 10,
34 | 3: (x) => x * 10,
35 | 4: (x) => x * 10,
36 | 5: (x) => x * 10,
37 | 6: (x) => (x - 90) * 1000 / 10,
38 | 7: (x) => (x - 3) / 0.002,
39 | 8: (x) => (x - 5) / 0.002,
40 | 9: (x) => (x - 7) / 0.001,
41 | 10: (x) => (x - 90) * 1000 / 10,
42 | 11: (x) => (85 - x) * 1000 / 20,
43 | 12: (x) => (x - 70) * 1000 / 15,
44 | 13: (x) => x * 10,
45 | 14: (x) => (85 - x) * 1000 / 15,
46 | 15: (x) => (x - 40) * 1000 / 30,
47 | 18: (x) => (x - 40) * 1000 / 30,
48 | 19: (x) => (x - 40) * 1000 / 30
49 | };
50 |
51 | export const engine_unitValueToValue = {
52 | 6: (x) => 20 * (x - 50),
53 | 10: (x) => 50 * (x - 80),
54 | 11: (x) => -50 * (x - 85),
55 | 12: (x) => (200 / 3) * (x - 70),
56 | 14: (x) => 50 * (x - 60),
57 | 18: (x) => 50 * (x - 50),
58 | 19: (x) => 50 * (x - 50),
59 | }
60 |
61 | export const downforce24UnitValueToValue = {
62 | 7: (x) => 497.6 * x - 1489.8,
63 | 8: (x) => 496.8 * x - 2479.5,
64 | 9: (x) => 974.048 * x - 6803.2614
65 | };
66 |
67 | export const parts = {
68 | 0: "engine",
69 | 3: "chassis",
70 | 4: "front_wing",
71 | 5: "rear_wing",
72 | 6: "sidepods",
73 | 7: "underfloor",
74 | 8: "suspension"
75 | };
76 |
77 | export const standardWeightPerPart = {
78 | 3: 5150,
79 | 4: 2625,
80 | 5: 3125,
81 | 6: 4125,
82 | 7: 3550,
83 | 8: 2900
84 | };
85 |
86 | export const standardBuildworkPerPart = {
87 | 3: 2000,
88 | 4: 500,
89 | 5: 500,
90 | 6: 1500,
91 | 7: 1500,
92 | 8: 1500
93 | };
94 |
95 | export const optimalWeightPerPart = {
96 | 3: 4070,
97 | 4: 1525,
98 | 5: 1945,
99 | 6: 3025,
100 | 7: 2390,
101 | 8: 1940
102 | };
103 |
104 | export const minimalWeightPerPart = {
105 | 3: 3800,
106 | 4: 1250,
107 | 5: 1650,
108 | 6: 2750,
109 | 7: 2100,
110 | 8: 1700
111 | };
112 |
113 | export const carAttributes = {
114 | 0: "top_speed",
115 | 1: "acceleration",
116 | 2: "drs",
117 | 3: "low_speed",
118 | 4: "medium_speed",
119 | 5: "high_speed",
120 | 6: "dirty_air",
121 | 7: "brake_cooling",
122 | 8: "engine_cooling"
123 | };
124 |
125 | export const statsMinMax = {
126 | 0: [0, 100],
127 | 1: [0, 100],
128 | 2: [0, 100],
129 | 3: [0, 100],
130 | 4: [0, 100],
131 | 5: [0, 100],
132 | 7: [3, 5],
133 | 8: [5, 7],
134 | 9: [7, 8],
135 | 10: [90, 100],
136 | 13: [0, 100]
137 | };
138 |
139 | export const lifespanPartsMinMax = {
140 | 3: [3800, 6500],
141 | 4: [1250, 4000],
142 | 5: [1650, 4600],
143 | 6: [2750, 5500],
144 | 7: [2100, 5000],
145 | 8: [1700, 4100]
146 | };
147 |
148 | export const attributesMinMax = {
149 | top_speed: [313.00, 328.00],
150 | acceleration: [1.800, 1.900],
151 | drs: [0, 100],
152 | low_speed: [2.000, 3.000],
153 | medium_speed: [3.000, 4.000],
154 | high_speed: [4.000, 5.500],
155 | dirty_air: [0, 100],
156 | brake_cooling: [0, 100],
157 | engine_cooling: [0, 100]
158 | };
159 |
160 | export const attributesUnits = {
161 | top_speed: "km/h",
162 | acceleration: "G",
163 | drs: "%",
164 | low_speed: "G",
165 | medium_speed: "G",
166 | high_speed: "G",
167 | dirty_air: "%",
168 | brake_cooling: "%",
169 | engine_cooling: "%"
170 | };
171 |
172 | export const attributesContributions = {
173 | top_speed: 0.15,
174 | acceleration: 0,
175 | drs: 0.15,
176 | low_speed: 0.1666,
177 | medium_speed: 0.1666,
178 | high_speed: 0.1666,
179 | dirty_air: 0.0666,
180 | brake_cooling: 0.0666,
181 | engine_cooling: 0.0666
182 | };
183 |
184 | export const attributesContributions2 = {
185 | top_speed: 0.15,
186 | acceleration: 0,
187 | drs: 0.15,
188 | low_speed: 0.2166,
189 | medium_speed: 0.2166,
190 | high_speed: 0.2166,
191 | dirty_air: 0.03,
192 | brake_cooling: 0.01,
193 | engine_cooling: 0.01
194 | };
195 |
196 | export const attributesContributions3 = {
197 | top_speed: 0.144,
198 | acceleration: 0.018,
199 | drs: 0.115,
200 | low_speed: 0.195,
201 | medium_speed: 0.195,
202 | high_speed: 0.195,
203 | dirty_air: 0.029,
204 | brake_cooling: 0.078,
205 | engine_cooling: 0.031
206 | };
207 |
208 | export const fuel_efficiency_factors = {
209 | 0: 1
210 | };
211 |
212 | export const power_factors = {
213 | 0: 1
214 | };
215 |
216 | export const performance_loss_factors = {
217 | 0: 1
218 | };
219 |
220 | export const performance_threshold_factors = {
221 | 0: 1
222 | };
223 |
224 | export const operational_range_factors = {
225 | 0: 1
226 | };
227 |
228 | export const lifespan_factors = {
229 | 1: 0,
230 | 2: 0,
231 | 3: 5,
232 | 4: 2,
233 | 5: 3,
234 | 6: 5,
235 | 7: 4,
236 | 8: 1
237 | };
238 |
239 | export const drag_reduction_factors = {
240 | 3: 0.2,
241 | 5: 0.3,
242 | 6: 0.2,
243 | 7: 0.2,
244 | 8: 0.1
245 | };
246 |
247 | export const engine_cooling_factors = {
248 | 3: 0.4,
249 | 6: 0.6
250 | };
251 |
252 | export const airflow_middle_factors = {
253 | 3: 0.6,
254 | 6: 0.4
255 | };
256 |
257 | export const airflow_front_factors = {
258 | 4: 0.4,
259 | 6: 0.2,
260 | 8: 0.4
261 | };
262 |
263 | export const airflow_sensitivity_factors = {
264 | 4: 0.4,
265 | 5: 0.4,
266 | 7: 0.2
267 | };
268 |
269 | export const brake_cooling_factors = {
270 | 4: 0.4,
271 | 8: 0.6
272 | };
273 |
274 | export const low_speed_downforce_factors = {
275 | 4: 0.2,
276 | 5: 0.2,
277 | 7: 0.3,
278 | 8: 0.3
279 | };
280 |
281 | export const medium_speed_downforce_factors = {
282 | 4: 0.2,
283 | 5: 0.2,
284 | 7: 0.5,
285 | 8: 0.1
286 | };
287 |
288 | export const high_speed_downforce_factors = {
289 | 4: 0.2,
290 | 5: 0.2,
291 | 7: 0.5,
292 | 8: 0.1
293 | };
294 |
295 | export const drs_delta_factors = {
296 | 5: 0.75,
297 | 3: 0.25
298 | };
299 |
300 | export const top_speed_contributors = {
301 | 4: 1
302 | };
303 |
304 | export const acceleration_contributors = {
305 | 10: 0.5,
306 | 4: 0.5,
307 | 16: 0.15
308 | };
309 |
310 | export const drs_contributors = {
311 | 3: 1
312 | };
313 |
314 | export const low_speed_contributors = {
315 | 0: 0.6,
316 | 7: 1,
317 | 16: 0.24
318 | };
319 |
320 | export const medium_speed_contributors = {
321 | 0: 0.4,
322 | 13: 0.4,
323 | 8: 1,
324 | 16: 0.27
325 | };
326 |
327 | export const high_speed_contributors = {
328 | 13: 0.6,
329 | 9: 1,
330 | 16: 0.24
331 | };
332 |
333 | export const dirty_air_contributors = {
334 | 1: 1
335 | };
336 |
337 | export const brake_cooling_contributors = {
338 | 2: 1
339 | };
340 |
341 | export const engine_cooling_contributors = {
342 | 5: 1
343 | };
344 |
--------------------------------------------------------------------------------
/src/js/backend/scriptUtils/countries.js:
--------------------------------------------------------------------------------
1 | export const countries_abreviations = {
2 | "Andorra": "AD",
3 | "United Arab Emirates": "AE",
4 | "Afghanistan": "AF",
5 | "Antigua and Barbuda": "AG",
6 | "Anguilla": "AI",
7 | "Albania": "AL",
8 | "Armenia": "AM",
9 | "Netherlands Antilles": "AN",
10 | "Angola": "AO",
11 | "Antarctica": "AQ",
12 | "Argentina": "AR",
13 | "American Samoa": "AS",
14 | "Austria": "AT",
15 | "Australia": "AU",
16 | "Aruba": "AW",
17 | "Åland Islands": "AX",
18 | "Azerbaijan": "AZ",
19 | "Bosnia and Herzegovina": "BA",
20 | "Barbados": "BB",
21 | "Bangladesh": "BD",
22 | "Belgium": "BE",
23 | "Burkina Faso": "BF",
24 | "Bulgaria": "BG",
25 | "Bahrain": "BH",
26 | "Burundi": "BI",
27 | "Benin": "BJ",
28 | "Saint Barthélemy": "BL",
29 | "Bermuda": "BM",
30 | "Brunei Darussalam": "BN",
31 | "Bolivia": "BO",
32 | "Brazil": "BR",
33 | "Bahamas": "BS",
34 | "Bhutan": "BT",
35 | "Bouvet Island": "BV",
36 | "Botswana": "BW",
37 | "Belarus": "BY",
38 | "Belize": "BZ",
39 | "Canada": "CA",
40 | "Cocos (Keeling) Islands": "CC",
41 | "Congo, Democratic Republic of the": "CD",
42 | "Central African Republic": "CF",
43 | "Congo, Republic of the": "CG",
44 | "Switzerland": "CH",
45 | "Côte d'Ivoire": "CI",
46 | "Cook Islands": "CK",
47 | "Chile": "CL",
48 | "Cameroon": "CM",
49 | "China": "CN",
50 | "Colombia": "CO",
51 | "Costa Rica": "CR",
52 | "Cuba": "CU",
53 | "Cape Verde": "CV",
54 | "Curaçao": "CW",
55 | "Christmas Island": "CX",
56 | "Cyprus": "CY",
57 | "Czech Republic": "CZ",
58 | "Germany": "DE",
59 | "Djibouti": "DJ",
60 | "Denmark": "DK",
61 | "Dominica": "DM",
62 | "Dominican Republic": "DO",
63 | "Algeria": "DZ",
64 | "Ecuador": "EC",
65 | "Estonia": "EE",
66 | "Egypt": "EG",
67 | "Western Sahara": "EH",
68 | "Eritrea": "ER",
69 | "Spain": "ES",
70 | "Ethiopia": "ET",
71 | "Finland": "FI",
72 | "Fiji": "FJ",
73 | "Falkland Islands (Malvinas)": "FK",
74 | "Micronesia, Federated States of": "FM",
75 | "Faroe Islands": "FO",
76 | "France": "FR",
77 | "Gabon": "GA",
78 | "United Kingdom": "GB",
79 | "Grenada": "GD",
80 | "Georgia": "GE",
81 | "French Guiana": "GF",
82 | "Guernsey": "GG",
83 | "Ghana": "GH",
84 | "Gibraltar": "GI",
85 | "Greenland": "GL",
86 | "Gambia": "GM",
87 | "Guinea": "GN",
88 | "Guadeloupe": "GP",
89 | "Equatorial Guinea": "GQ",
90 | "Greece": "GR",
91 | "South Georgia and the South Sandwich Islands": "GS",
92 | "Guatemala": "GT",
93 | "Guam": "GU",
94 | "Guinea-Bissau": "GW",
95 | "Guyana": "GY",
96 | "Hong Kong": "HK",
97 | "Heard Island and McDonald Islands": "HM",
98 | "Honduras": "HN",
99 | "Croatia": "HR",
100 | "Haiti": "HT",
101 | "Hungary": "HU",
102 | "Indonesia": "ID",
103 | "Ireland": "IE",
104 | "Israel": "IL",
105 | "Isle of Man": "IM",
106 | "India": "IN",
107 | "British Indian Ocean Territory": "IO",
108 | "Iraq": "IQ",
109 | "Iran, Islamic Republic of": "IR",
110 | "Iceland": "IS",
111 | "Italy": "IT",
112 | "Jersey": "JE",
113 | "Jamaica": "JM",
114 | "Jordan": "JO",
115 | "Japan": "JP",
116 | "Kenya": "KE",
117 | "Kyrgyzstan": "KG",
118 | "Cambodia": "KH",
119 | "Kiribati": "KI",
120 | "Comoros": "KM",
121 | "Saint Kitts and Nevis": "KN",
122 | "Korea, Democratic People's Republic of": "KP",
123 | "Korea, Republic of": "KR",
124 | "Kuwait": "KW",
125 | "Cayman Islands": "KY",
126 | "Kazakhstan": "KZ",
127 | "Lao People's Democratic Republic": "LA",
128 | "Lebanon": "LB",
129 | "Saint Lucia": "LC",
130 | "Liechtenstein": "LI",
131 | "Sri Lanka": "LK",
132 | "Liberia": "LR",
133 | "Lesotho": "LS",
134 | "Lithuania": "LT",
135 | "Luxembourg": "LU",
136 | "Latvia": "LV",
137 | "Libya": "LY",
138 | "Morocco": "MA",
139 | "Monaco": "MC",
140 | "Moldova, Republic of": "MD",
141 | "Montenegro": "ME",
142 | "Saint Martin (French part)": "MF",
143 | "Madagascar": "MG",
144 | "Marshall Islands": "MH",
145 | "Macedonia, the Former Yugoslav Republic of": "MK",
146 | "Mali": "ML",
147 | "Myanmar": "MM",
148 | "Mongolia": "MN",
149 | "Macao": "MO",
150 | "Northern Mariana Islands": "MP",
151 | "Martinique": "MQ",
152 | "Mauritania": "MR",
153 | "Montserrat": "MS",
154 | "Malta": "MT",
155 | "Mauritius": "MU",
156 | "Maldives": "MV",
157 | "Malawi": "MW",
158 | "Mexico": "MX",
159 | "Malaysia": "MY",
160 | "Mozambique": "MZ",
161 | "Namibia": "NA",
162 | "New Caledonia": "NC",
163 | "Niger": "NE",
164 | "Norfolk Island": "NF",
165 | "Nigeria": "NG",
166 | "Nicaragua": "NI",
167 | "Netherlands": "NL",
168 | "Norway": "NO",
169 | "Nepal": "NP",
170 | "Nauru": "NR",
171 | "Niue": "NU",
172 | "New Zealand": "NZ",
173 | "Oman": "OM",
174 | "Panama": "PA",
175 | "Peru": "PE",
176 | "French Polynesia": "PF",
177 | "Papua New Guinea": "PG",
178 | "Philippines": "PH",
179 | "Pakistan": "PK",
180 | "Poland": "PL",
181 | "Saint Pierre and Miquelon": "PM",
182 | "Pitcairn": "PN",
183 | "Puerto Rico": "PR",
184 | "Palestine, State of": "PS",
185 | "Portugal": "PT",
186 | "Palau": "PW",
187 | "Paraguay": "PY",
188 | "Qatar": "QA",
189 | "Réunion": "RE",
190 | "Romania": "RO",
191 | "Serbia": "RS",
192 | "Russian": "RU",
193 | "Rwanda": "RW",
194 | "Saudi Arabia": "SA",
195 | "Solomon Islands": "SB",
196 | "Seychelles": "SC",
197 | "Sudan": "SD",
198 | "Sweden": "SE",
199 | "Singapore": "SG",
200 | "Saint Helena, Ascension and Tristan da Cunha": "SH",
201 | "Slovenia": "SI",
202 | "Svalbard and Jan Mayen": "SJ",
203 | "Slovakia": "SK",
204 | "Sierra Leone": "SL",
205 | "San Marino": "SM",
206 | "Senegal": "SN",
207 | "Somalia": "SO",
208 | "Suriname": "SR",
209 | "South Sudan": "SS",
210 | "Sao Tome and Principe": "ST",
211 | "El Salvador": "SV",
212 | "Sint Maarten (Dutch part)": "SX",
213 | "Syrian Arab Republic": "SY",
214 | "Swaziland": "SZ",
215 | "Turks and Caicos Islands": "TC",
216 | "Chad": "TD",
217 | "French Southern Territories": "TF",
218 | "Togo": "TG",
219 | "Thailand": "TH",
220 | "Tajikistan": "TJ",
221 | "Tokelau": "TK",
222 | "Timor-Leste": "TL",
223 | "Turkmenistan": "TM",
224 | "Tunisia": "TN",
225 | "Tonga": "TO",
226 | "Turkey": "TR",
227 | "Trinidad and Tobago": "TT",
228 | "Tuvalu": "TV",
229 | "Taiwan, Province of China": "TW",
230 | "Tanzania, United Republic of": "TZ",
231 | "Ukraine": "UA",
232 | "Uganda": "UG",
233 | "United States Minor Outlying Islands": "UM",
234 | "United States": "US",
235 | "Uruguay": "UY",
236 | "Uzbekistan": "UZ",
237 | "Holy See (Vatican City State)": "VA",
238 | "Saint Vincent and the Grenadines": "VC",
239 | "Venezuela": "VE",
240 | "Virgin Islands, British": "VG",
241 | "Virgin Islands, U.S.": "VI",
242 | "Vietnam": "VN",
243 | "Vanuatu": "VU",
244 | "Wallis and Futuna": "WF",
245 | "Samoa": "WS",
246 | "Yemen": "YE",
247 | "Mayotte": "YT",
248 | "South Africa": "ZA",
249 | "Zambia": "ZM",
250 | "Zimbabwe": "ZW"
251 | }
252 |
253 |
--------------------------------------------------------------------------------
/src/js/backend/scriptUtils/editTeamUtils.js:
--------------------------------------------------------------------------------
1 | import { queryDB } from "../dbManager";
2 |
3 | export function fetchTeamData(teamID){
4 | const levCon = queryDB(`
5 | SELECT BuildingID, DegradationValue
6 | FROM Buildings_HQ
7 | WHERE TeamID = ${teamID}
8 | `, 'allRows') || [];
9 |
10 | const data = levCon.map(row => [row[0], parseFloat(Number(row[1]).toFixed(2))]);
11 | if (teamID == "32") data.push(["160", 1]);
12 |
13 | const daySeason = queryDB(`
14 | SELECT Day, CurrentSeason
15 | FROM Player_State
16 | `, 'singleRow');
17 |
18 | const days = queryDB(`
19 | SELECT MIN(Day), MAX(Day)
20 | FROM Seasons_Deadlines
21 | WHERE SeasonID = ${daySeason[1]}
22 | `, 'singleRow');
23 |
24 | const costCap = queryDB(`
25 | SELECT SUM(value) AS Value
26 | FROM Finance_Transactions
27 | WHERE Day >= ${days[0]}
28 | AND Day < ${days[1]}
29 | AND AffectsCostCap = 1
30 | AND TeamID = ${teamID}
31 | `, 'allRows');
32 |
33 | const teamBalance = queryDB(`
34 | SELECT Balance
35 | FROM Finance_TeamBalance
36 | WHERE TeamID = ${teamID}
37 | `, 'singleRow');
38 |
39 | const seasonObj = queryDB(`
40 | SELECT TargetPos
41 | FROM Board_SeasonObjectives
42 | WHERE TeamID = ${teamID}
43 | AND SeasonID = ${daySeason[1]}
44 | `, 'singleRow');
45 |
46 | const maxTargetYear = queryDB(`
47 | SELECT MAX(TargetEndYear)
48 | FROM Board_Objectives
49 | WHERE TeamID = ${teamID}
50 | `, 'singleRow');
51 |
52 | const longTermObj = queryDB(`
53 | SELECT Type, TargetEndYear
54 | FROM Board_Objectives
55 | WHERE TeamID = ${teamID}
56 | AND TargetEndYear = ${maxTargetYear[0]}
57 | `, 'singleRow');
58 |
59 | const playerTeam = queryDB(`
60 | SELECT TeamID
61 | FROM Player
62 | `, 'singleRow');
63 |
64 | let confidence;
65 | if (playerTeam[0] == Number(teamID)) {
66 | confidence = queryDB(`
67 | SELECT Confidence
68 | FROM Board_Confidence
69 | WHERE Season = ${daySeason[1]}
70 | `, 'singleRow') || [-1];
71 | } else {
72 | confidence = [-1];
73 | }
74 |
75 | const pitStats = queryDB(`
76 | SELECT StatID, Val
77 | FROM Staff_PitCrew_PerformanceStats
78 | WHERE TeamID = ${teamID}
79 | `, 'allRows') || [];
80 |
81 | const pitDict = {};
82 | pitStats.forEach(stat => {
83 | pitDict[stat[0]] = parseFloat(Number(stat[1]).toFixed(2));
84 | });
85 |
86 | const engineId = queryDB(`SELECT engineId FROM Custom_Engine_Allocations WHERE teamId = ${teamID}`, 'singleValue');
87 | const allEngines = queryDB(`SELECT * FROM Custom_Engine_Allocations`, 'allRows');
88 |
89 | data.push(seasonObj, longTermObj, teamBalance, costCap, confidence, daySeason[1], pitDict, engineId);
90 | return data;
91 | }
92 |
93 | // manageCostCap(teamID, amount)
94 | export function manageCostCap(teamID, amount) {
95 | let remaining = parseInt(amount, 10);
96 |
97 | if (remaining > 0) {
98 | while (remaining > 0) {
99 | // Obtenemos la transacción negativa más reciente
100 | const transaction = queryDB(`
101 | SELECT ROWID, Value, Reference
102 | FROM Finance_Transactions
103 | WHERE TeamID = ${teamID}
104 | AND AffectsCostCap = 1
105 | AND Value < 0
106 | ORDER BY Day DESC, ROWID DESC
107 | LIMIT 1
108 | `, 'singleRow');
109 |
110 | if (!transaction) {
111 | break;
112 | } else {
113 | const rowid = transaction[0];
114 | const value = transaction[1];
115 | // reference = transaction[2]; // no se usa directamente
116 |
117 | let amountToAdd;
118 | if ((value + remaining) <= 0) {
119 | amountToAdd = remaining;
120 | } else {
121 | amountToAdd = -value;
122 | }
123 |
124 | queryDB(`
125 | UPDATE Finance_Transactions
126 | SET Value = Value + ${amountToAdd}
127 | WHERE ROWID = ${rowid}
128 | `);
129 |
130 | remaining -= amountToAdd;
131 | }
132 | }
133 | }
134 | // Si remaining <= 0, insertamos una transacción que incremente el CostCap (o lo modifique negativamente)
135 | else {
136 | const daySeason = queryDB(`
137 | SELECT Day, CurrentSeason
138 | FROM Player_State
139 | `, 'singleRow');
140 |
141 | queryDB(`
142 | INSERT INTO Finance_Transactions
143 | VALUES (${teamID}, ${daySeason[0]}, ${amount}, 9, -1, 1)
144 | `);
145 | }
146 | }
147 |
148 | export function editTeam(info) {
149 | const daySeason = queryDB(`
150 | SELECT Day, CurrentSeason
151 | FROM Player_State
152 | `, 'singleRow');
153 |
154 | const teamID = info.teamID;
155 |
156 | // Actualización de Buildings_HQ
157 | info.facilities.forEach(facility => {
158 | const id = facility[0].slice(0, -1);
159 | // facility[0] podría ser "160a", por ejemplo, y con slice(0, -1) quitas el último carácter
160 |
161 | queryDB(`
162 | UPDATE Buildings_HQ
163 | SET BuildingID = '${facility[0]}',
164 | DegradationValue = ${facility[1]}
165 | WHERE TeamID = ${teamID}
166 | AND BuildingType = ${id}
167 | `);
168 | });
169 |
170 | // Board_SeasonObjectives
171 | queryDB(`
172 | UPDATE Board_SeasonObjectives
173 | SET TargetPos = ${info.seasonObj}
174 | WHERE TeamID = ${teamID}
175 | AND SeasonID = ${daySeason[1]}
176 | `);
177 |
178 | // Board_Objectives (objetivo a largo plazo)
179 | const maxTargetYear = queryDB(`
180 | SELECT MAX(TargetEndYear)
181 | FROM Board_Objectives
182 | WHERE TeamID = ${teamID}
183 | `, 'singleRow');
184 |
185 | queryDB(`
186 | UPDATE Board_Objectives
187 | SET Type = ${info.longTermObj},
188 | TargetEndYear = ${info.longTermYear}
189 | WHERE TeamID = ${teamID}
190 | AND TargetEndYear = ${maxTargetYear[0]}
191 | `);
192 |
193 | // Board_Confidence
194 | if (info.confidence !== "-1") {
195 | queryDB(`
196 | UPDATE Board_Confidence
197 | SET Confidence = ${info.confidence}
198 | WHERE Season = ${daySeason[1]}
199 | `);
200 | }
201 |
202 | // Finance_TeamBalance
203 | queryDB(`
204 | UPDATE Finance_TeamBalance
205 | SET Balance = ${info.teamBudget}
206 | WHERE TeamID = ${teamID}
207 | `);
208 |
209 | // Ajuste de CostCap
210 | manageCostCap(teamID, info.costCapEdit);
211 |
212 | // Actualizar Staff_PitCrew_PerformanceStats
213 | Object.keys(info.pitCrew).forEach(statID => {
214 | queryDB(`
215 | UPDATE Staff_PitCrew_PerformanceStats
216 | SET Val = ${info.pitCrew[statID]}
217 | WHERE TeamID = ${teamID}
218 | AND StatID = ${statID}
219 | `);
220 | });
221 |
222 | // La parte de manage_engine_change la manejas tú
223 | manage_engine_change(teamID, info.engine);
224 | }
225 |
226 | export function manage_engine_change(teamID, engineId) {
227 |
228 | const oldEngineId = queryDB(`SELECT DesignID FROM Parts_Designs WHERE TeamID = ${teamID} AND PartType = 0`, 'singleValue');
229 | const oldERSId = queryDB(`SELECT DesignID FROM Parts_Designs WHERE TeamID = ${teamID} AND PartType = 1`, 'singleValue');
230 | const oldGearboxId = queryDB(`SELECT DesignID FROM Parts_Designs WHERE TeamID = ${teamID} AND PartType = 2`, 'singleValue');
231 |
232 | const nmewERSId = parseInt(engineId, 10) + 1;
233 | const newGearboxId = parseInt(engineId, 10) + 2;
234 |
235 | const newEngineStats = queryDB(`SELECT partStat, unitValue, Value FROM Custom_Engines_Stats WHERE designId = ${engineId}`, 'allRows');
236 | const newERSStats = queryDB(`SELECT partStat, unitValue, Value FROM Custom_Engines_Stats WHERE designId = ${nmewERSId}`, 'singleRow');
237 | const newGearboxStats = queryDB(`SELECT partStat, unitValue, Value FROM Custom_Engines_Stats WHERE designId = ${newGearboxId}`, 'singleRow');
238 |
239 | const engineStats = queryDB(`SELECT PartStat FROM Parts_Designs_StatValues WHERE DesignID = ${oldEngineId}`, 'allRows');
240 |
241 | engineStats.forEach(stat => {
242 | const newStat = newEngineStats.find(newStat => newStat[0] === stat[0]);
243 | if (newStat) {
244 | queryDB(`UPDATE Parts_Designs_StatValues SET Value = ${newStat[2]}, UnitValue = ${newStat[1]} WHERE DesignID = ${oldEngineId} AND PartStat = ${stat[0]}`);
245 | }
246 | });
247 |
248 | queryDB(`UPDATE Parts_Designs_StatValues SET Value = ${newERSStats[2]}, UnitValue = ${newERSStats[1]} WHERE DesignID = ${oldERSId} AND PartStat = 15`);
249 | queryDB(`UPDATE Parts_Designs_StatValues SET Value = ${newGearboxStats[2]}, UnitValue = ${newGearboxStats[1]} WHERE DesignID = ${oldGearboxId} AND PartStat = 15`);
250 |
251 | if (parseInt(engineId) <= 10){
252 | const year = queryDB(`SELECT CurrentSeason FROM Player_State`, 'singleValue');
253 | const newEngineManufacturer = queryDB(`SELECT Value FROM Parts_Enum_EngineManufacturers WHERE EngineDesignID = ${engineId}`, 'singleValue');
254 | queryDB(`UPDATE Parts_TeamHistory SET EngineManufacturer = ${newEngineManufacturer} WHERE TeamID = ${teamID} AND SeasonID = ${year}`);
255 | }
256 |
257 | queryDB(`UPDATE Custom_Engine_Allocations SET engineId = ${engineId} WHERE teamId = ${teamID}`);
258 |
259 |
260 | }
--------------------------------------------------------------------------------
/src/js/backend/scriptUtils/eidtStatsUtils.js:
--------------------------------------------------------------------------------
1 | import { queryDB } from "../dbManager";
2 |
3 | // Constantes para referencias en la edición de mentalidad
4 | export const driverStats = [2, 3, 4, 5, 6, 7, 8, 9, 10];
5 |
6 | export const mentalityAreas = {
7 | 0: [5, 11, 13, 9],
8 | 1: [0, 2, 6, 7, 8, 14],
9 | 2: [1, 3, 4, 12, 10]
10 | };
11 |
12 | export const mentalityEvents = {
13 | 0: [1, 7, 10, 13, 15, 19],
14 | 1: [2, 11, 12, 14, 16, 20, 21],
15 | 2: [0, 3, 4, 5, 6, 8, 9, 17, 18]
16 | };
17 |
18 | export const mentalityOpinions = {
19 | 0: 10,
20 | 1: 3,
21 | 2: 0,
22 | 3: -4,
23 | 4: -10
24 | };
25 |
26 | export const mentalityOverall = {
27 | 0: 95,
28 | 1: 79,
29 | 2: 59,
30 | 3: 24,
31 | 4: 5
32 | };
33 |
34 | // Editar estadísticas de un Staff (driver o staff general)
35 | export function editStats(driverID, type, stats, retirement, driverNum, wants1) {
36 | //creat sttasParasm from stats string to an array
37 | const statsParams = stats.split(" ");
38 |
39 |
40 | if (type === "0") {
41 | const isStats = queryDB(`
42 | SELECT *
43 | FROM Staff_performanceStats
44 | WHERE StaffID = ${driverID}
45 | `, 'singleRow');
46 |
47 | if (isStats) {
48 | queryDB(`
49 | UPDATE Staff_performanceStats
50 | SET Val = CASE StatID
51 | WHEN 2 THEN ${statsParams[0]}
52 | WHEN 3 THEN ${statsParams[1]}
53 | WHEN 4 THEN ${statsParams[2]}
54 | WHEN 5 THEN ${statsParams[3]}
55 | WHEN 6 THEN ${statsParams[4]}
56 | WHEN 7 THEN ${statsParams[5]}
57 | WHEN 8 THEN ${statsParams[6]}
58 | WHEN 9 THEN ${statsParams[7]}
59 | WHEN 10 THEN ${statsParams[8]}
60 | ELSE Val
61 | END
62 | WHERE StaffID = ${driverID}
63 | `);
64 | } else {
65 | const statsArray = statsParams.slice(2, 11);
66 | statsArray.forEach((newStat, i) => {
67 | const statID = driverStats[i];
68 | queryDB(`
69 | INSERT INTO Staff_performanceStats (StaffID, StatID, Val, Max)
70 | VALUES (${driverID}, ${statID}, ${newStat}, 100)
71 | `);
72 | });
73 | }
74 | queryDB(`
75 | UPDATE Staff_DriverData
76 | SET Improvability = ${statsParams[9]}, Aggression = ${statsParams[10]}
77 | WHERE StaffID = ${driverID}
78 | `);
79 | queryDB(`
80 | UPDATE Staff_GameData
81 | SET RetirementAge = ${retirement}
82 | WHERE StaffID = ${driverID}
83 | `);
84 |
85 | changeDriverNumber(driverID, driverNum);
86 |
87 | queryDB(`
88 | UPDATE Staff_DriverData
89 | SET WantsChampionDriverNumber = ${wants1}
90 | WHERE StaffID = ${driverID}
91 | `);
92 | }
93 | else if (type === "1") {
94 | queryDB(`
95 | UPDATE Staff_performanceStats
96 | SET Val = CASE StatID
97 | WHEN 0 THEN ${statsParams[0]}
98 | WHEN 1 THEN ${statsParams[1]}
99 | WHEN 14 THEN ${statsParams[2]}
100 | WHEN 15 THEN ${statsParams[3]}
101 | WHEN 16 THEN ${statsParams[4]}
102 | WHEN 17 THEN ${statsParams[5]}
103 | ELSE Val
104 | END
105 | WHERE StaffID = ${driverID}
106 | `);
107 | queryDB(`
108 | UPDATE Staff_GameData
109 | SET RetirementAge = ${retirement}
110 | WHERE StaffID = ${driverID}
111 | `);
112 | }
113 | else if (type === "2") {
114 | queryDB(`
115 | UPDATE Staff_performanceStats
116 | SET Val = CASE StatID
117 | WHEN 13 THEN ${statsParams[0]}
118 | WHEN 25 THEN ${statsParams[1]}
119 | WHEN 43 THEN ${statsParams[2]}
120 | ELSE Val
121 | END
122 | WHERE StaffID = ${driverID}
123 | `);
124 | queryDB(`
125 | UPDATE Staff_GameData
126 | SET RetirementAge = ${retirement}
127 | WHERE StaffID = ${driverID}
128 | `);
129 | }
130 | else if (type === "3") {
131 | queryDB(`
132 | UPDATE Staff_performanceStats
133 | SET Val = CASE StatID
134 | WHEN 19 THEN ${statsParams[0]}
135 | WHEN 20 THEN ${statsParams[1]}
136 | WHEN 26 THEN ${statsParams[2]}
137 | WHEN 27 THEN ${statsParams[3]}
138 | WHEN 28 THEN ${statsParams[4]}
139 | WHEN 29 THEN ${statsParams[5]}
140 | WHEN 30 THEN ${statsParams[6]}
141 | WHEN 31 THEN ${statsParams[7]}
142 | ELSE Val
143 | END
144 | WHERE StaffID = ${driverID}
145 | `);
146 | queryDB(`
147 | UPDATE Staff_GameData
148 | SET RetirementAge = ${retirement}
149 | WHERE StaffID = ${driverID}
150 | `);
151 | }
152 | else if (type === "4") {
153 | queryDB(`
154 | UPDATE Staff_performanceStats
155 | SET Val = CASE StatID
156 | WHEN 11 THEN ${statsParams[0]}
157 | WHEN 22 THEN ${statsParams[1]}
158 | WHEN 23 THEN ${statsParams[2]}
159 | WHEN 24 THEN ${statsParams[3]}
160 | ELSE Val
161 | END
162 | WHERE StaffID = ${driverID}
163 | `);
164 | queryDB(`
165 | UPDATE Staff_GameData
166 | SET RetirementAge = ${retirement}
167 | WHERE StaffID = ${driverID}
168 | `);
169 | }
170 | }
171 |
172 | export function changeDriverNumber(driverID, newNumber) {
173 | const oldNum = queryDB(`
174 | SELECT Number
175 | FROM Staff_DriverNumbers
176 | WHERE CurrentHolder = ${driverID}
177 | `, 'singleValue');
178 | if (oldNum) {
179 | queryDB(`
180 | UPDATE Staff_DriverNumbers
181 | SET CurrentHolder = NULL
182 | WHERE Number = ${oldNum}
183 | `);
184 | }
185 | const oldHolderOfNum = queryDB(`
186 | SELECT CurrentHolder
187 | FROM Staff_DriverNumbers
188 | WHERE Number = ${newNumber}
189 | `, 'singleValue');
190 | if (oldHolderOfNum) {
191 | const emptyNumbers = queryDB(`
192 | SELECT Number FROM Staff_DriverNumbers
193 | WHERE CurrentHolder IS NULL
194 | `, 'allRows');
195 | if (emptyNumbers.length) {
196 | const randomNum = emptyNumbers[Math.floor(Math.random() * emptyNumbers.length)][0];
197 |
198 | queryDB(`
199 | UPDATE Staff_DriverNumbers SET CurrentHolder = ${oldHolderOfNum} WHERE Number = ${randomNum}
200 | `);
201 | }
202 | }
203 | queryDB(`
204 | UPDATE Staff_DriverNumbers
205 | SET CurrentHolder = ${driverID}
206 | WHERE Number = ${newNumber}
207 | `);
208 | }
209 |
210 | export function editName(driverID, newName) {
211 | const parts = newName.split(" ");
212 | const newFirstName = parts[0];
213 | const newLastName = parts.slice(1).join(" ");
214 | const stringLiteralFirstName = `[STRING_LITERAL:Value=|${newFirstName}|]`;
215 | const stringLiteralLastName = `[STRING_LITERAL:Value=|${newLastName}|]`;
216 | queryDB(`
217 | UPDATE Staff_BasicData
218 | SET FirstName = '${stringLiteralFirstName}',
219 | LastName = '${stringLiteralLastName}'
220 | WHERE StaffID = ${driverID}
221 | `);
222 | }
223 |
224 | export function editCode(driverID, newCode) {
225 | const stringLiteralCode = `[STRING_LITERAL:Value=|${newCode}|]`;
226 | queryDB(`
227 | UPDATE Staff_DriverData
228 | SET DriverCode = '${stringLiteralCode}'
229 | WHERE StaffID = ${driverID}
230 | `);
231 | }
232 |
233 | // Helpers de fechas
234 | export function excelToDate(excelDate) {
235 | const baseDate = new Date(1899, 11, 30);
236 | const ms = excelDate * 86400000;
237 | return new Date(baseDate.getTime() + ms);
238 | }
239 |
240 | export function dateToExcel(date) {
241 | const baseDate = new Date(1899, 11, 30);
242 | const diff = date.getTime() - baseDate.getTime();
243 | return Math.floor(diff / 86400000);
244 | }
245 |
246 | export function changeYearsInExcelDate(excelDate, years) {
247 | const oldDate = excelToDate(excelDate);
248 | let newYear = oldDate.getFullYear() + years;
249 | let newDate = new Date(oldDate.getTime());
250 | newDate.setFullYear(newYear);
251 | if (newDate.getMonth() !== oldDate.getMonth()) {
252 | newDate = new Date(newYear, 1, 28);
253 | }
254 | const newExcelDate = dateToExcel(newDate);
255 | return { newDate, newExcelDate };
256 | }
257 |
258 | export function editAge(driverID, ageGap) {
259 | const driverBirthdate = queryDB(`
260 | SELECT DOB
261 | FROM Staff_BasicData
262 | WHERE StaffID = ${driverID}
263 | `, 'singleValue');
264 | const { newDate, newExcelDate } = changeYearsInExcelDate(driverBirthdate, parseInt(ageGap, 10));
265 | const y = newDate.getFullYear();
266 | const m = newDate.getMonth() + 1;
267 | const d = newDate.getDate();
268 | queryDB(`
269 | UPDATE Staff_BasicData
270 | SET DOB = ${newExcelDate},
271 | DOB_ISO = '${y}-${m}-${d}'
272 | WHERE StaffID = ${driverID}
273 | `);
274 | }
275 |
276 | export function editMentality(driverID, mentalityStr) {
277 | if (mentalityStr !== -1) {
278 | const mentalityArray = mentalityStr.split(" ");
279 | let sum = 0;
280 | mentalityArray.forEach((value, area) => {
281 | queryDB(`
282 | UPDATE Staff_Mentality_AreaOpinions
283 | SET Opinion = ${value}
284 | WHERE StaffID = ${driverID}
285 | AND Category = ${area}
286 | `);
287 | const statuses = mentalityAreas[area];
288 | const events = mentalityEvents[area];
289 | sum += parseInt(value, 10);
290 | statuses.forEach(status => {
291 | queryDB(`
292 | UPDATE Staff_Mentality_Statuses
293 | SET Opinion = ${value},
294 | Value = ${mentalityOpinions[value]}
295 | WHERE StaffID = ${driverID}
296 | AND Status = ${status}
297 | `);
298 | });
299 | events.forEach(ev => {
300 | queryDB(`
301 | UPDATE Staff_Mentality_Events
302 | SET Opinion = ${value},
303 | Value = ${mentalityOpinions[value]}
304 | WHERE StaffID = ${driverID}
305 | AND Event = ${ev}
306 | `);
307 | });
308 | });
309 | const average = Math.floor(sum / 3);
310 | queryDB(`
311 | UPDATE Staff_State
312 | SET Mentality = ${mentalityOverall[average]},
313 | MentalityOpinion = ${average}
314 | WHERE StaffID = ${driverID}
315 | `);
316 | }
317 | }
318 |
319 | export function editRetirement(driverID, value) {
320 | queryDB(`
321 | UPDATE Staff_GameData
322 | SET Retired = ${value}
323 | WHERE StaffID = ${driverID}
324 | `);
325 | }
326 |
327 | export function editSuperlicense(driverID, value) {
328 | queryDB(`
329 | UPDATE Staff_DriverData
330 | SET HasSuperLicense = ${value},
331 | HasRacedEnoughToJoinF1 = ${value}
332 | WHERE StaffID = ${driverID}
333 | `);
334 | }
335 |
336 | export function editMarketability(driverID, value) {
337 | queryDB(`
338 | UPDATE Staff_DriverData
339 | SET Marketability = ${value}
340 | WHERE StaffID = ${driverID}
341 | `);
342 | }
343 |
344 | export function editFreezeMentality(state) {
345 | if (state === 0) {
346 | queryDB(`DROP TRIGGER IF EXISTS update_Opinion_After_Insert;`);
347 | queryDB(`DROP TRIGGER IF EXISTS update_Opinion_After_Update;`);
348 | queryDB(`DROP TRIGGER IF EXISTS clear_Staff_Mentality_Statuses;`);
349 | queryDB(`DROP TRIGGER IF EXISTS clear_Staff_Mentality_AreaOpinions;`);
350 | queryDB(`DROP TRIGGER IF EXISTS clear_Staff_Mentality_Events;`);
351 | queryDB(`DROP TRIGGER IF EXISTS reset_Staff_State;`);
352 | } else {
353 | queryDB(`
354 | CREATE TRIGGER IF NOT EXISTS update_Opinion_After_Insert
355 | AFTER INSERT ON Staff_Mentality_AreaOpinions
356 | BEGIN
357 | UPDATE Staff_Mentality_AreaOpinions
358 | SET Opinion = 2
359 | WHERE Opinion != 2;
360 | END;
361 | `);
362 | queryDB(`
363 | CREATE TRIGGER IF NOT EXISTS update_Opinion_After_Update
364 | AFTER UPDATE OF Opinion ON Staff_Mentality_AreaOpinions
365 | BEGIN
366 | UPDATE Staff_Mentality_AreaOpinions
367 | SET Opinion = 2
368 | WHERE Opinion != 2;
369 | END;
370 | `);
371 | queryDB(`
372 | CREATE TRIGGER IF NOT EXISTS clear_Staff_Mentality_Statuses
373 | AFTER INSERT ON Staff_Mentality_Statuses
374 | BEGIN
375 | DELETE FROM Staff_Mentality_Statuses;
376 | END;
377 | `);
378 | queryDB(`
379 | CREATE TRIGGER IF NOT EXISTS clear_Staff_Mentality_Events
380 | AFTER INSERT ON Staff_Mentality_Events
381 | BEGIN
382 | DELETE FROM Staff_Mentality_Events;
383 | END;
384 | `);
385 | queryDB(`
386 | CREATE TRIGGER IF NOT EXISTS reset_Staff_State
387 | AFTER UPDATE ON Staff_State
388 | BEGIN
389 | UPDATE Staff_State
390 | SET Mentality = 50, MentalityOpinion = 2;
391 | END;
392 | `);
393 | }
394 | }
395 |
--------------------------------------------------------------------------------
/src/js/frontend/dragFile.js:
--------------------------------------------------------------------------------
1 | // dragDrop.js
2 | import { analyzeFileToDatabase } from "../backend/UESaveHandler";
3 | import { setDatabase, queryDB } from "../backend/dbManager.js";
4 | import { gamePill, editorPill, setSaveName, new_update_notifications, setIsShowingNotification } from "./renderer.js";
5 | import { Command } from "../backend/command.js";
6 | import { getCombinedDict } from "./config.js";
7 |
8 | let carAnalysisUtils = null;
9 | export const dbWorker = new Worker(new URL('../backend/worker.js', import.meta.url));
10 |
11 | const dropDiv = document.getElementById("dropDiv");
12 |
13 | dropDiv.addEventListener("dragenter", (event) => {
14 | event.preventDefault();
15 | dropDiv.classList.add("drag-over");
16 | });
17 |
18 | dropDiv.addEventListener("dragover", (event) => {
19 | event.preventDefault();
20 | event.dataTransfer.dropEffect = "copy";
21 | });
22 |
23 | dropDiv.addEventListener("dragleave", (event) => {
24 | event.preventDefault();
25 | dropDiv.classList.remove("drag-over");
26 | });
27 |
28 | dropDiv.addEventListener("drop", async (event) => {
29 | event.preventDefault();
30 | dropDiv.classList.remove("drag-over");
31 |
32 | const file = event.dataTransfer.files[0];
33 | if (file.name.split('.').pop() === "vdf") {
34 | console.error("File not supported");
35 | new_update_notifications(
36 | 'File type not supported. See this video to find your save file.',
37 | "error"
38 | );
39 | return;
40 | }
41 | else if (file.name.split('.').pop() === "sav") {
42 | const footerNotification = document.querySelector('.footer-notification');
43 | if (footerNotification.classList.contains('error')) {
44 | console.log("has error")
45 | footerNotification.classList.remove('show');
46 | setIsShowingNotification(false);
47 | }
48 | setSaveName(file.name);
49 | if (!file) return;
50 |
51 |
52 | await new Promise((resolve, reject) => {
53 | dbWorker.postMessage(
54 | { command: 'loadDB', data: { file: file } },
55 | );
56 |
57 | dbWorker.onmessage = (msg) => {
58 | if (msg.data.responseMessage === "Database loaded") {
59 | console.log("[Main Thread] Database loaded in Worker");
60 | const dateObj = new Date(msg.data.content);
61 | const day = dateObj.getDate();
62 | //get month in text
63 | const month = new Intl.DateTimeFormat('en-US', { month: 'long' }).format(dateObj);
64 | const year = dateObj.getFullYear();
65 | //get the day with the st. nd. rd. th. suffix
66 | const completeDay = day + (day % 10 == 1 && day != 11 ? "st" : day % 10 == 2 && day != 12 ? "nd" : day % 10 == 3 && day != 13 ? "rd" : "th");
67 | document.querySelector("#dateDay").textContent = completeDay;
68 | document.querySelector("#dateMonth").textContent = month;
69 | document.querySelector("#dateYear").textContent = year;
70 | resolve(); // Continuamos cuando la base de datos esté cargada
71 | } else if (msg.data.error) {
72 | console.error("[Main Thread] Error loading DB:", msg.data.error);
73 | reject(new Error(msg.data.error));
74 | }
75 | };
76 | });
77 |
78 | document.getElementById("saveFileDropped").classList.add("completed");
79 | editorPill.classList.remove("d-none");
80 | gamePill.classList.remove("d-none");
81 |
82 |
83 | const command = new Command("saveSelected", {});
84 | command.execute();
85 | }
86 | });
87 |
88 |
89 |
90 |
--------------------------------------------------------------------------------
/src/js/frontend/public_key.js:
--------------------------------------------------------------------------------
1 | export const PUBLIC_KEY = `
2 | -----BEGIN PUBLIC KEY-----
3 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlT8C5VV5NNW904PoJxyC
4 | Y7r92/FbncLfOShBpfgrl1gNRsb1OJ9gxid/KiPFbNDIzaMb7UU8weO9XQ4YG6MN
5 | jYu5Zj/kFueud1CknDdep+9s88ctPLmgS7hxwrY43G7AclDW2YLUExTvceJTwvui
6 | 1UGic+28YfMAO8YTnb+HG+zZDpg4sxHHP+DuM4HA11tcrfR33/kGrYj4lyzYhLk+
7 | CJcEREXyUVi37z3WW5ZCRlrN6K3J2Z16ZvYYRpXss8ogRgNRL3NvFeqU7ETh5FGG
8 | FYLgcp2yepKCmNnrvGpd09MaikJ1QzEa9wH+aZP9V5WdhKMBm/TNoa1Qg4OmWJ+I
9 | hQIDAQAB
10 | -----END PUBLIC KEY-----
11 | `
--------------------------------------------------------------------------------
/vercel.json:
--------------------------------------------------------------------------------
1 | {
2 | "headers": [
3 | {
4 | "source": "/assets/images/(.*)",
5 | "headers": [
6 | {
7 | "key": "Cache-Control",
8 | "value": "public, max-age=31536000, immutable"
9 | }
10 | ]
11 | }
12 | ]
13 | }
14 |
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const HtmlWebpackPlugin = require('html-webpack-plugin');
3 | const MiniCssExtractPlugin = require('mini-css-extract-plugin');
4 | const CopyWebpackPlugin = require('copy-webpack-plugin');
5 | const webpack = require('webpack');
6 | const packageJson = require('./package.json');
7 |
8 |
9 | module.exports = {
10 | mode: 'production',
11 |
12 | entry: './src/index.js', // Archivo de entrada principal
13 |
14 | output: {
15 | path: path.resolve(__dirname, 'dist'),
16 | filename: 'bundle.js',
17 | },
18 |
19 | plugins: [
20 | new HtmlWebpackPlugin({
21 | template: './src/index.html', // Path a tu HTML original
22 | filename: 'index.html' // El HTML generado irá a 'dist/index.html'
23 | }),
24 | new MiniCssExtractPlugin({
25 | filename: 'styles.css',
26 | }),
27 | new CopyWebpackPlugin({
28 | patterns: [
29 | {
30 | from: 'assets/images', // ajusta esta ruta a donde tengas tus imágenes
31 | to: 'assets/images'
32 | }
33 | ]
34 | }),
35 | new webpack.DefinePlugin({
36 | APP_VERSION: JSON.stringify(packageJson.version),
37 | }),
38 | ],
39 |
40 | resolve: {
41 | extensions: ['.js'],
42 | fallback: {
43 | // Si tu código usa 'buffer' (p.ej. new Buffer o Buffer.from)
44 | buffer: require.resolve('buffer/'),
45 | "vm": false,
46 | "stream": false,
47 | "fs": false,
48 | "path": false,
49 | "crypto": false,
50 | },
51 | },
52 |
53 | module: {
54 | rules: [
55 | {
56 | test: /\.js$/,
57 | exclude: /node_modules/,
58 | use: {
59 | loader: 'babel-loader',
60 | },
61 | },
62 | {
63 | test: /\.css$/i,
64 | use: [MiniCssExtractPlugin.loader, 'css-loader'], // Usa el plugin en lugar de 'style-loader'
65 | },
66 | {
67 | test: /\.(png|jpe?g|gif|svg)$/i,
68 | type: 'asset/resource',
69 | generator: {
70 | filename: 'assets/images/[name][ext]', // Copia las imágenes en dist/assets/images
71 | },
72 | },
73 | ],
74 | },
75 | };
--------------------------------------------------------------------------------