├── .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 | ![Image](https://github.com/user-attachments/assets/b9163ea4-b304-40b7-ab11-98404306719e) 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 | image 15 | 16 | 2. Editing driver/staff contracts, and adding contracts for the future 17 | 18 | image 19 | 20 | 3. Check out what drivers/staff have pre-contracts signed (see Russell, Tsolov and Marti with then notification icon) 21 | 22 | image 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 | image 28 | 29 | 6. Customize your calendar, including weather of races 30 | 31 | image 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 | image 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 | image 45 | image 46 | 47 | 13. Edit the performance and durability of any of the 4 engines suppliers 48 | 49 | image 50 | 51 | 14. Add NEW CUSTOM ENGINES 52 | 53 | image 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 | image 58 | 59 | 16. Keep track of past seasons with a wikipedia-style table for drivers and teams championships 60 | 61 | image 62 | 63 | 17. Check records (wins, poles, podiums, WDCs) from previous seasons or compare them with all-time greats 64 | 65 | image 66 | 67 | 18. Compare drivers and teamas and their stats from their past seasons using the Head To Head graphic 68 | 69 | image 70 | 71 | 19. Follow stories from your save with the **NEWS TAB** 72 | 73 | image 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 | 7 | 8 | 10 | 15 | 20 | 25 | 30 | 31 | 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 | Patreon logo - Brandlogos.net -------------------------------------------------------------------------------- /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 | }; --------------------------------------------------------------------------------