├── .editorconfig
├── .gitattributes
├── .gitignore
├── 010 Editor Binary Template
├── AuroraAssetTemplate.bt
└── FSDAssetTemplate.bt
├── AuroraAssetEditor.sln
├── AuroraAssetEditor
├── App.xaml
├── App.xaml.cs
├── AuroraAsset.dll
├── AuroraAssetEditor.csproj
├── Classes
│ ├── AuroraAsset.cs
│ ├── AuroraAssetDll.cs
│ ├── AuroraDbManager.cs
│ ├── FSDAsset.cs
│ ├── FTPOperations.cs
│ ├── InternetArchiveAssetDownloader.cs
│ ├── StatusArgs.cs
│ ├── ThreadSafeObservableCollection.cs
│ ├── WpfControlThreadingExtensions.cs
│ ├── XboxAssetDownloader.cs
│ └── XboxUnity.cs
├── Controls
│ ├── BackgroundControl.xaml
│ ├── BackgroundControl.xaml.cs
│ ├── BoxartControl.xaml
│ ├── BoxartControl.xaml.cs
│ ├── CircularProgressBar.xaml
│ ├── CircularProgressBar.xaml.cs
│ ├── FtpAssetsControl.xaml
│ ├── FtpAssetsControl.xaml.cs
│ ├── IconBannerControl.xaml
│ ├── IconBannerControl.xaml.cs
│ ├── OnlineAssetsControl.xaml
│ ├── OnlineAssetsControl.xaml.cs
│ ├── ScreenshotsControl.xaml
│ └── ScreenshotsControl.xaml.cs
├── Helpers
│ └── GlobalState.cs
├── InputDialog.xaml
├── InputDialog.xaml.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Models
│ └── Game.cs
├── Resources
│ ├── Placeholders
│ │ ├── background.png
│ │ ├── banner.png
│ │ ├── cover.png
│ │ ├── icon.png
│ │ └── screenshot.png
│ └── icon.ico
├── TitleAndDbIdDialog.xaml
├── TitleAndDbIdDialog.xaml.cs
└── msvcr100.dll
├── CHANGELOG.txt
├── LICENSE
├── README.md
├── SQLite.Interop
└── x86
│ └── SQLite.Interop.dll
├── appveyor.yml
└── tools
└── ParseLocale.py
/.editorconfig:
--------------------------------------------------------------------------------
1 | #
2 | # https://editorconfig.org/#file-format-details
3 | # https://docs.microsoft.com/en-us/visualstudio/ide/cpp-editorconfig-properties?view=vs-2019
4 | # https://stackoverflow.com/questions/51356453/export-visual-studios-code-style-settings-as-editorconfig
5 | #
6 |
7 | #####################
8 | ### Core Settings ###
9 | #####################
10 | root = true
11 |
12 | # All files
13 | [*]
14 |
15 | end_of_line = crlf
16 | trim_trailing_whitespace = true
17 | insert_final_newline = true
18 |
19 | [*.cs]
20 |
21 | charset = utf-8-bom
22 |
23 | indent_style = tab
24 | indent_size = 4
25 | tab_width = 4
26 |
27 | #### .NET Coding Conventions ####
28 |
29 | # Organize usings
30 | dotnet_separate_import_directive_groups = false
31 | dotnet_sort_system_directives_first = true
32 | file_header_template = unset
33 |
34 | # this. and Me. preferences
35 | dotnet_style_qualification_for_event = false
36 | dotnet_style_qualification_for_field = false
37 | dotnet_style_qualification_for_method = false
38 | dotnet_style_qualification_for_property = false
39 |
40 | # Language keywords vs BCL types preferences
41 | dotnet_style_predefined_type_for_locals_parameters_members = false:silent
42 | dotnet_style_predefined_type_for_member_access = false:silent
43 |
44 | # Parentheses preferences
45 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
46 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
47 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
48 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
49 |
50 | # Modifier preferences
51 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
52 |
53 | # Expression-level preferences
54 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
55 | dotnet_style_coalesce_expression = true:suggestion
56 | dotnet_style_null_propagation = true:suggestion
57 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
58 | dotnet_style_prefer_auto_properties = true:silent
59 | dotnet_style_object_initializer = true:suggestion
60 | dotnet_style_collection_initializer = true:suggestion
61 | dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
62 | dotnet_style_prefer_conditional_expression_over_assignment = true:silent
63 | dotnet_style_prefer_conditional_expression_over_return = true:silent
64 | dotnet_style_explicit_tuple_names = true:suggestion
65 | dotnet_style_prefer_inferred_tuple_names = false:suggestion
66 | dotnet_style_prefer_inferred_anonymous_type_member_names = false:suggestion
67 | dotnet_style_prefer_compound_assignment = true:suggestion
68 | dotnet_style_prefer_simplified_interpolation = true:suggestion
69 | dotnet_style_namespace_match_folder = true:suggestion
70 |
71 | # Field preferences
72 | dotnet_style_readonly_field = true:suggestion
73 |
74 | # Parameter preferences
75 | dotnet_code_quality_unused_parameters = all:suggestion
76 |
77 | # Suppression preferences
78 | dotnet_remove_unnecessary_suppression_exclusions = none
79 |
80 | # New line preferences
81 | dotnet_style_allow_multiple_blank_lines_experimental = true:silent
82 | dotnet_style_allow_statement_immediately_after_block_experimental = true:silent
83 |
84 | #### C# Coding Conventions ####
85 |
86 | csharp_style_prefer_top_level_statements = true:silent
87 | csharp_style_prefer_tuple_swap = true:suggestion
88 | csharp_style_prefer_utf8_string_literals = true:suggestion
89 | csharp_style_prefer_readonly_struct = true:suggestion
90 | csharp_style_prefer_readonly_struct_member = true:suggestion
91 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true:silent
92 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true:silent
93 |
94 | # var preferences
95 | csharp_style_var_elsewhere = false:silent
96 | csharp_style_var_for_built_in_types = false:silent
97 | csharp_style_var_when_type_is_apparent = false:silent
98 |
99 | # Expression-bodied members
100 | csharp_style_expression_bodied_accessors = true:silent
101 | csharp_style_expression_bodied_constructors = false:silent
102 | csharp_style_expression_bodied_indexers = true:silent
103 | csharp_style_expression_bodied_lambdas = true:silent
104 | csharp_style_expression_bodied_local_functions = when_on_single_line:silent
105 | csharp_style_expression_bodied_methods = when_on_single_line:silent
106 | csharp_style_expression_bodied_operators = true:silent
107 | csharp_style_expression_bodied_properties = true:silent
108 |
109 | # Pattern matching preferences
110 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
111 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
112 | csharp_style_prefer_not_pattern = true:suggestion
113 | csharp_style_prefer_pattern_matching = true:silent
114 | csharp_style_prefer_switch_expression = true:suggestion
115 | csharp_style_prefer_extended_property_pattern = true:suggestion
116 |
117 | # Null-checking preferences
118 | csharp_style_conditional_delegate_call = true:suggestion
119 |
120 | # Modifier preferences
121 | csharp_prefer_static_local_function = true:suggestion
122 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
123 |
124 | # Code-block preferences
125 | csharp_prefer_braces = true:silent
126 | csharp_prefer_simple_using_statement = true:suggestion
127 | csharp_style_namespace_declarations = block_scoped:silent
128 | csharp_style_prefer_method_group_conversion = true:silent
129 |
130 | # Expression-level preferences
131 | csharp_prefer_simple_default_expression = true:suggestion
132 | csharp_style_deconstructed_variable_declaration = false:suggestion
133 | csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
134 | csharp_style_inlined_variable_declaration = true:suggestion
135 | csharp_style_pattern_local_over_anonymous_function = true
136 | csharp_style_prefer_index_operator = true:suggestion
137 | csharp_style_prefer_range_operator = true:suggestion
138 | csharp_style_throw_expression = true:suggestion
139 | csharp_style_unused_value_assignment_preference = discard_variable:suggestion
140 | csharp_style_unused_value_expression_statement_preference = discard_variable:silent
141 | csharp_style_prefer_null_check_over_type_check = true:suggestion
142 | csharp_style_prefer_local_over_anonymous_function = true:suggestion
143 |
144 | # 'using' directive preferences
145 | csharp_using_directive_placement = outside_namespace:silent
146 |
147 | # New line preferences
148 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true:silent
149 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true:silent
150 | csharp_style_allow_embedded_statements_on_same_line_experimental = true:silent
151 |
152 | #### C# Formatting Rules ####
153 |
154 | # New line preferences
155 | csharp_new_line_before_catch = true
156 | csharp_new_line_before_else = true
157 | csharp_new_line_before_finally = true
158 | csharp_new_line_before_members_in_anonymous_types = true
159 | csharp_new_line_before_members_in_object_initializers = true
160 | csharp_new_line_before_open_brace = all
161 | csharp_new_line_between_query_expression_clauses = true
162 |
163 | # Indentation preferences
164 | csharp_indent_block_contents = true
165 | csharp_indent_braces = false
166 | csharp_indent_case_contents = true
167 | csharp_indent_case_contents_when_block = false
168 | csharp_indent_labels = one_less_than_current
169 | csharp_indent_switch_labels = true
170 |
171 | # Space preferences
172 | csharp_space_after_cast = false
173 | csharp_space_after_colon_in_inheritance_clause = true
174 | csharp_space_after_comma = true
175 | csharp_space_after_dot = false
176 | csharp_space_after_keywords_in_control_flow_statements = true
177 | csharp_space_after_semicolon_in_for_statement = true
178 | csharp_space_around_binary_operators = before_and_after
179 | csharp_space_around_declaration_statements = false
180 | csharp_space_before_colon_in_inheritance_clause = true
181 | csharp_space_before_comma = false
182 | csharp_space_before_dot = false
183 | csharp_space_before_open_square_brackets = false
184 | csharp_space_before_semicolon_in_for_statement = false
185 | csharp_space_between_empty_square_brackets = false
186 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
187 | csharp_space_between_method_call_name_and_opening_parenthesis = false
188 | csharp_space_between_method_call_parameter_list_parentheses = false
189 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
190 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
191 | csharp_space_between_method_declaration_parameter_list_parentheses = false
192 | csharp_space_between_parentheses = false
193 | csharp_space_between_square_brackets = false
194 |
195 | # Wrapping preferences
196 | csharp_preserve_single_line_blocks = true
197 | csharp_preserve_single_line_statements = true
198 |
199 | #### Naming styles ####
200 |
201 | # Naming rules
202 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
203 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
204 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
205 |
206 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
207 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
208 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
209 |
210 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
211 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
212 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
213 |
214 | # Symbol specifications
215 | dotnet_naming_symbols.interface.applicable_kinds = interface
216 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
217 | dotnet_naming_symbols.interface.required_modifiers =
218 |
219 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
220 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
221 | dotnet_naming_symbols.types.required_modifiers =
222 |
223 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
224 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
225 | dotnet_naming_symbols.non_field_members.required_modifiers =
226 |
227 | # Naming styles
228 | dotnet_naming_style.pascal_case.required_prefix =
229 | dotnet_naming_style.pascal_case.required_suffix =
230 | dotnet_naming_style.pascal_case.word_separator =
231 | dotnet_naming_style.pascal_case.capitalization = pascal_case
232 |
233 | dotnet_naming_style.begins_with_i.required_prefix = I
234 | dotnet_naming_style.begins_with_i.required_suffix =
235 | dotnet_naming_style.begins_with_i.word_separator =
236 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
237 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | x64/
19 | x86/
20 | build/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studo 2015 cache/options directory
26 | .vs/
27 |
28 | # MSTest test Results
29 | [Tt]est[Rr]esult*/
30 | [Bb]uild[Ll]og.*
31 |
32 | # NUNIT
33 | *.VisualState.xml
34 | TestResult.xml
35 |
36 | # Build Results of an ATL Project
37 | [Dd]ebugPS/
38 | [Rr]eleasePS/
39 | dlldata.c
40 |
41 | *_i.c
42 | *_p.c
43 | *_i.h
44 | *.ilk
45 | *.meta
46 | *.obj
47 | *.pch
48 | *.pdb
49 | *.pgc
50 | *.pgd
51 | *.rsp
52 | *.sbr
53 | *.tlb
54 | *.tli
55 | *.tlh
56 | *.tmp
57 | *.tmp_proj
58 | *.log
59 | *.vspscc
60 | *.vssscc
61 | .builds
62 | *.pidb
63 | *.svclog
64 | *.scc
65 |
66 | # Chutzpah Test files
67 | _Chutzpah*
68 |
69 | # Visual C++ cache files
70 | ipch/
71 | *.aps
72 | *.ncb
73 | *.opensdf
74 | *.sdf
75 | *.cachefile
76 |
77 | # Visual Studio profiler
78 | *.psess
79 | *.vsp
80 | *.vspx
81 |
82 | # TFS 2012 Local Workspace
83 | $tf/
84 |
85 | # Guidance Automation Toolkit
86 | *.gpState
87 |
88 | # ReSharper is a .NET coding add-in
89 | _ReSharper*/
90 | *.[Rr]e[Ss]harper
91 | *.DotSettings.user
92 |
93 | # JustCode is a .NET coding addin-in
94 | .JustCode
95 |
96 | # TeamCity is a build add-in
97 | _TeamCity*
98 |
99 | # DotCover is a Code Coverage Tool
100 | *.dotCover
101 |
102 | # NCrunch
103 | _NCrunch_*
104 | .*crunch*.local.xml
105 |
106 | # MightyMoose
107 | *.mm.*
108 | AutoTest.Net/
109 |
110 | # Web workbench (sass)
111 | .sass-cache/
112 |
113 | # Installshield output folder
114 | [Ee]xpress/
115 |
116 | # DocProject is a documentation generator add-in
117 | DocProject/buildhelp/
118 | DocProject/Help/*.HxT
119 | DocProject/Help/*.HxC
120 | DocProject/Help/*.hhc
121 | DocProject/Help/*.hhk
122 | DocProject/Help/*.hhp
123 | DocProject/Help/Html2
124 | DocProject/Help/html
125 |
126 | # Click-Once directory
127 | publish/
128 |
129 | # Publish Web Output
130 | *.[Pp]ublish.xml
131 | *.azurePubxml
132 | # TODO: Comment the next line if you want to checkin your web deploy settings
133 | # but database connection strings (with potential passwords) will be unencrypted
134 | *.pubxml
135 | *.publishproj
136 |
137 | # NuGet Packages
138 | *.nupkg
139 | # The packages folder can be ignored because of Package Restore
140 | **/packages/*
141 | # except build/, which is used as an MSBuild target.
142 | !**/packages/build/
143 | # Uncomment if necessary however generally it will be regenerated when needed
144 | #!**/packages/repositories.config
145 |
146 | # Windows Azure Build Output
147 | csx/
148 | *.build.csdef
149 |
150 | # Windows Store app package directory
151 | AppPackages/
152 |
153 | # Others
154 | *.[Cc]ache
155 | ClientBin/
156 | [Ss]tyle[Cc]op.*
157 | ~$*
158 | *~
159 | *.dbmdl
160 | *.dbproj.schemaview
161 | *.pfx
162 | *.publishsettings
163 | node_modules/
164 | bower_components/
165 |
166 | # RIA/Silverlight projects
167 | Generated_Code/
168 |
169 | # Backup & report files from converting an old project file
170 | # to a newer Visual Studio version. Backup files are not needed,
171 | # because we have git ;-)
172 | _UpgradeReport_Files/
173 | Backup*/
174 | UpgradeLog*.XML
175 | UpgradeLog*.htm
176 |
177 | # SQL Server files
178 | *.mdf
179 | *.ldf
180 |
181 | # Business Intelligence projects
182 | *.rdl.data
183 | *.bim.layout
184 | *.bim_*.settings
185 |
186 | # Microsoft Fakes
187 | FakesAssemblies/
188 |
189 | # Node.js Tools for Visual Studio
190 | .ntvs_analysis.dat
191 |
192 | # Visual Studio 6 build log
193 | *.plg
194 |
195 | # Visual Studio 6 workspace options file
196 | *.opt
197 | /AuroraAssetEditor/*.DotSettings
198 |
--------------------------------------------------------------------------------
/010 Editor Binary Template/AuroraAssetTemplate.bt:
--------------------------------------------------------------------------------
1 | //--------------------------------------
2 | //--- 010 Editor v5.0.2 Binary Template
3 | //
4 | // File: AuroraAssetTemplate.bt
5 | // Author: MasterRowen & Swizzy
6 | // Revision: 1
7 | // Purpose: Aurora's .asset files
8 | //--------------------------------------
9 | #define ASSET_SCREENSHOT_MAX 20
10 |
11 | typedef enum {
12 | ASSET_ICON,
13 | ASSET_BANNER,
14 | ASSET_BOXART, // Covers basically
15 | ASSET_SLOT,
16 | ASSET_BACKGROUND,
17 | ASSET_SCREENSHOT_START,
18 | ASSET_SCREENSHOT = ASSET_SCREENSHOT_START,
19 | ASSET_SCREENSHOT_END = ASSET_SCREENSHOT_START + ASSET_SCREENSHOT_MAX,
20 | ASSET_END = ASSET_SCREENSHOT_END,
21 | ASSET_MAX = ASSET_END
22 | } ASSET_TYPE;
23 |
24 | typedef struct {
25 | DWORD dwMagic; // Should be 'RXEA'
26 | DWORD dwVersion; // Should be 1
27 | DWORD dwDataSize;
28 | } ASSET_PACK_HEADER;
29 |
30 | typedef struct {
31 | DWORD Common;
32 | DWORD ReferenceCount; // Should be 0
33 | DWORD Fence; // Should be 0
34 | DWORD ReadFence; // Should be 0
35 | DWORD Identifier; // Should be 0
36 | DWORD BaseFlush; // Should be 0xFFFF0000
37 | DWORD MipFlush; // Should be 0xFFFF0000
38 | union {
39 | struct {
40 | struct {
41 | DWORD Tiled : 1; // BOOL
42 | DWORD Pitch : 9; // DWORD
43 | DWORD : 1;
44 | DWORD : 2;
45 | DWORD ClampZ : 3; // GPUCLAMP
46 | DWORD ClampY : 3; // GPUCLAMP
47 | DWORD ClampX : 3; // GPUCLAMP
48 | DWORD SignW : 2; // GPUSIGN
49 | DWORD SignZ : 2; // GPUSIGN
50 | DWORD SignY : 2; // GPUSIGN
51 | DWORD SignX : 2; // GPUSIGN
52 | DWORD Type : 2; // GPUCONSTANTTYPE
53 | } GPU_FETCH_CONSTANT_0;
54 | struct {
55 | DWORD BaseAddress : 20; // DWORD
56 | DWORD ClampPolicy : 1; // GPUCLAMPPOLICY
57 | DWORD Stack : 1; // BOOL
58 | DWORD RequestSize : 2; // GPUREQUESTSIZE
59 | DWORD Endian : 2; // GPUENDIAN
60 | DWORD DataFormat : 6; // GPUTEXTUREFORMAT
61 | } GPU_FETCH_CONSTANT_1;
62 | union
63 | {
64 | struct {
65 | DWORD : 8;
66 | DWORD Width : 24; // DWORD
67 | } OneD;
68 | struct {
69 | DWORD : 6;
70 | DWORD Height : 13; // DWORD
71 | DWORD Width : 13; // DWORD
72 | } TwoD;
73 | struct {
74 | DWORD Depth : 10; // DWORD
75 | DWORD Height : 11; // DWORD
76 | DWORD Width : 11; // DWORD
77 | } ThreeD;
78 | struct {
79 | DWORD Depth : 6; // DWORD
80 | DWORD Height : 13; // DWORD
81 | DWORD Width : 13; // DWORD
82 | } Stack;
83 | } Size;
84 | struct {
85 | DWORD BorderSize : 1; // DWORD
86 | DWORD : 3;
87 | DWORD AnisoFilter : 3; // GPUANISOFILTER
88 | DWORD MipFilter : 2; // GPUMIPFILTER
89 | DWORD MinFilter : 2; // GPUMINMAGFILTER
90 | DWORD MagFilter : 2; // GPUMINMAGFILTER
91 | INT ExpAdjust : 6; // int
92 | DWORD SwizzleW : 3; // GPUSWIZZLE
93 | DWORD SwizzleZ : 3; // GPUSWIZZLE
94 | DWORD SwizzleY : 3; // GPUSWIZZLE
95 | DWORD SwizzleX : 3; // GPUSWIZZLE
96 | DWORD NumFormat : 1; // GPUNUMFORMAT
97 | } GPUTEXTURE_FETCH_CONSTANT_3;
98 | struct {
99 | INT GradExpAdjustV : 5; // int
100 | INT GradExpAdjustH : 5; // int
101 | INT LODBias : 10; // int
102 | DWORD MinAnisoWalk : 1; // BOOL
103 | DWORD MagAnisoWalk : 1; // BOOL
104 | DWORD MaxMipLevel : 4; // DWORD
105 | DWORD MinMipLevel : 4; // DWORD
106 | DWORD VolMinFilter : 1; // GPUMINMAGFILTER
107 | DWORD VolMagFilter : 1; // GPUMINMAGFILTER
108 | } GPUTEXTURE_FETCH_CONSTANT_4;
109 | struct {
110 | DWORD MipAddress : 20; // DWORD
111 | DWORD PackedMips : 1; // BOOL
112 | DWORD Dimension : 2; // GPUDIMENSION
113 | INT AnisoBias : 4; // int
114 | DWORD TriClamp : 2; // GPUTRICLAMP
115 | DWORD ForceBCWToMax : 1; // BOOL
116 | DWORD BorderColor : 2; // GPUBORDERCOLOR
117 | } GPUTEXTURE_FETCH_CONSTANT_5;
118 | } GPUTEXTURE_FETCH_CONSTANT;
119 | DWORD dword[6];
120 | } GPU_FETCH_CONSTANT;
121 | } ASSET_PACK_TEXTURE_HEADER;
122 |
123 | typedef struct {
124 | DWORD Offset;
125 | DWORD Size;
126 | DWORD ExtendedInfo; // It's a pointer, ignore the value, or save it as 0
127 | ASSET_PACK_TEXTURE_HEADER TextureHeader;
128 | } ASSET_PACK_ENTRY;
129 |
130 | typedef struct {
131 | DWORD Flags;
132 | DWORD ScreenshotCount;
133 | ASSET_PACK_ENTRY Entries[ASSET_MAX];
134 | } ASSET_PACK_ENTRY_TABLE;
135 |
136 | typedef struct {
137 | BYTE ALPHA;
138 | BYTE RED;
139 | BYTE GREEN;
140 | BYTE BLUE;
141 | } COLOR;
142 |
143 | // Actual shown data starts here
144 |
145 | ASSET_PACK_HEADER Header;
146 | Assert(Header.dwMagic == 0x52584541, "Invalid header Magic, expected 'RXEA'");
147 | Assert(Header.dwVersion == 1, "Invalid header Version, expected 1");
148 | ASSET_PACK_ENTRY_TABLE EntryTable;
149 | BYTE Padding[2048 - (FTell() % 2048)];
150 | local ASSET_TYPE x;
151 | for( x = 0; x < ASSET_MAX; x++ ) {
152 | if( EntryTable.Entries[x].Size > 0 ) {
153 | typedef struct {
154 | COLOR ImageData[EntryTable.Entries[x].Size/sizeof(COLOR)];
155 | } IMAGE_DATA;
156 | IMAGE_DATA Image;
157 | }
158 | }
--------------------------------------------------------------------------------
/010 Editor Binary Template/FSDAssetTemplate.bt:
--------------------------------------------------------------------------------
1 | //--------------------------------------
2 | //--- 010 Editor v5.0.2 Binary Template
3 | //
4 | // File: FSDAssetTemplate.bt
5 | // Author: Swizzy, Special thanks to MaesterRowen for providing the information within
6 | // Revision: 1
7 | // Purpose: FSD's .asset files
8 | //--------------------------------------
9 |
10 | typedef enum {
11 | Thumbnail = 0x01,
12 | Background = 0x02,
13 | Banner = 0x04,
14 | Boxart = 0x08,
15 | Preview = 0x10,
16 | Screenshot = 0x20,
17 | Slot = 0x40,
18 | FullCover = 0x80
19 | } CONTENT_ASSET_TYPES;
20 |
21 | typedef struct {
22 | DWORD Magic;
23 | DWORD Version;
24 | DWORD Reserved;
25 | DWORD AssetFlags;
26 | DWORD AssetCount;
27 | DWORD ScreenshotCount;
28 | } CONTENT_ASSET_HEADER;
29 |
30 | typedef struct {
31 | CONTENT_ASSET_TYPES AssetType;
32 | DWORD Offset;
33 | DWORD Size;
34 | DWORD TotalSize;
35 | } CONTENT_ASSET_ENTRY;
36 |
37 | typedef struct _CONTENT_ASSET_SCREENSHOT_ENTRY {
38 | DWORD Offset;
39 | DWORD Size;
40 | DWORD TotalSize;
41 | DWORD Reserved;
42 | } CONTENT_ASSET_SCREENSHOT_ENTRY;
43 |
44 | CONTENT_ASSET_HEADER Header;
45 | Assert(Header.Magic == 0x46534441, "Invalid header Magic, expected 'RXEA'");
46 | Assert(Header.Version == 1, "Invalid header Version, expected 1");
47 | CONTENT_ASSET_ENTRY Entries[10];
48 | CONTENT_ASSET_SCREENSHOT_ENTRY Screenshots[20];
--------------------------------------------------------------------------------
/AuroraAssetEditor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.6.33829.357
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuroraAssetEditor", "AuroraAssetEditor\AuroraAssetEditor.csproj", "{7EBB95B2-CB56-4263-9C2B-601E0FC36001}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{E7B130E9-33B7-48A5-BDC8-B2DB74B0128D}"
9 | ProjectSection(SolutionItems) = preProject
10 | .editorconfig = .editorconfig
11 | appveyor.yml = appveyor.yml
12 | EndProjectSection
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Debug|x86 = Debug|x86
18 | Release|Any CPU = Release|Any CPU
19 | Release|x86 = Release|x86
20 | EndGlobalSection
21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
22 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Debug|x86.ActiveCfg = Debug|x86
25 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Debug|x86.Build.0 = Debug|x86
26 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Release|x86.ActiveCfg = Release|x86
29 | {7EBB95B2-CB56-4263-9C2B-601E0FC36001}.Release|x86.Build.0 = Release|x86
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {6E09DD1D-9C76-46F0-B0F2-B665D49308B6}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/App.xaml:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/App.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // App.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 08/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor {
9 | using System.Drawing;
10 | using System.IO;
11 | using System.Reflection;
12 | using System.Windows;
13 | using System.Windows.Interop;
14 | using System.Windows.Media;
15 | using System.Windows.Media.Imaging;
16 | using Classes;
17 |
18 | ///
19 | /// Interaction logic for App.xaml
20 | ///
21 | public partial class App {
22 | internal static readonly FtpOperations FtpOperations = new FtpOperations();
23 |
24 | private static readonly Icon Icon =
25 | Icon.ExtractAssociatedIcon(Path.Combine(Path.GetDirectoryName(Assembly.GetAssembly(typeof(App)).Location), Path.GetFileName(Assembly.GetAssembly(typeof(App)).Location)));
26 |
27 | internal static readonly ImageSource WpfIcon = Imaging.CreateBitmapSourceFromHIcon(Icon.Handle, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
28 |
29 | private void AppStart(object sender, StartupEventArgs e) { new MainWindow(e.Args).Show(); }
30 | }
31 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/AuroraAsset.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/AuroraAsset.dll
--------------------------------------------------------------------------------
/AuroraAssetEditor/AuroraAssetEditor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | WinExe
6 | true
7 | net48
8 |
9 | AnyCPU;x86
10 | AnyCPU
11 |
12 | AuroraAssetEditor
13 | AuroraAssetEditor
14 | AuroraAssetEditor
15 | Copyright 2015-2023
16 | https://github.com/XboxUnity/AuroraAssetEditor
17 | v
18 | $(VersionPrefix)1.4.0.0
19 | 1.4.0.0
20 | 1.4.0.0
21 | 1.4.0.0
22 | 1.4.0.0
23 | Resources\icon.ico
24 | ..\bin\
25 |
26 |
27 |
28 | True
29 |
30 |
31 | True
32 |
33 |
34 | True
35 |
36 |
37 | True
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | PreserveNewest
69 |
70 |
71 | PreserveNewest
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/AuroraAsset.cs:
--------------------------------------------------------------------------------
1 | //
2 | // AuroraAsset.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 04/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Drawing;
12 | using System.Drawing.Imaging;
13 | using System.Linq;
14 | using System.Runtime.InteropServices;
15 | using System.Text;
16 | using PhoenixTools;
17 |
18 | public static class AuroraAsset {
19 | public enum AssetType {
20 | Icon, // Icon
21 | Banner, // Banner
22 | Boxart, // Cover
23 | Slot, // NXEArt, currently not used
24 | Background, // Background
25 | ScreenshotStart, // Screenshot 1
26 | ScreenshotEnd = ScreenshotStart + ScreenShotMax, // Screenshot 20
27 | Max = ScreenshotEnd // End of it all
28 | }
29 |
30 | private const int ScreenShotMax = 20;
31 |
32 | private static uint Swap(uint x) { return (x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 | (x & 0x00FF0000) >> 8 | (x & 0xFF000000) >> 24; }
33 |
34 | private static Image RawArgbToImage(byte[] raw, int width, int height) {
35 | var ret = new Bitmap(width, height);
36 | var rect = new Rectangle(new Point(0, 0), new Size(width, height));
37 | var bmpData = ret.LockBits(rect, ImageLockMode.ReadWrite, ret.PixelFormat);
38 | Marshal.Copy(raw, 0, bmpData.Scan0, raw.Length);
39 | ret.UnlockBits(bmpData);
40 | return ret;
41 | }
42 |
43 | private static byte[] ImageToRawArgb(Image img) {
44 | var bmp = new Bitmap(img);
45 | var ret = new byte[bmp.Height * bmp.Width * 4];
46 | var i = 0;
47 | for(var y = 0; y < bmp.Height; y++) {
48 | for(var x = 0; x < bmp.Width; x++) {
49 | var c = bmp.GetPixel(x, y);
50 | ret[i] = c.A;
51 | ret[i + 1] = c.R;
52 | ret[i + 2] = c.G;
53 | ret[i + 3] = c.B;
54 | i += 4;
55 | }
56 | }
57 | return ret;
58 | }
59 |
60 | public class AssetFile {
61 | public readonly int DataOffset;
62 | public readonly AssetPackEntryTable EntryTable;
63 | public readonly AssetPackHeader Header;
64 |
65 | public AssetFile() {
66 | Header = new AssetPackHeader(0x52584541, 1, 0);
67 | EntryTable = new AssetPackEntryTable();
68 | DataOffset = 20 + (EntryTable.Entries.Length * 64);
69 | DataOffset += 2048 - (DataOffset % 2048);
70 | }
71 |
72 | public AssetFile(byte[] data) {
73 | if(data == null) {
74 | Header = new AssetPackHeader(0x52584541, 1, 0);
75 | EntryTable = new AssetPackEntryTable();
76 | DataOffset = 20 + (EntryTable.Entries.Length * 64);
77 | DataOffset += 2048 - (DataOffset % 2048);
78 | return;
79 | }
80 | if(data.Length < 2048)
81 | throw new Exception("Invalid asset file size!");
82 | var magic = Swap(BitConverter.ToUInt32(data, 0));
83 | if(magic != 0x52584541)
84 | throw new Exception("Invalid asset file magic!");
85 | var version = Swap(BitConverter.ToUInt32(data, 4));
86 | if(version != 1)
87 | throw new NotSupportedException("Unsupported asset file version!");
88 | var datasize = Swap(BitConverter.ToUInt32(data, 8));
89 | Header = new AssetPackHeader(magic, version, datasize);
90 | EntryTable = new AssetPackEntryTable(data, 12);
91 | DataOffset = 20 + (EntryTable.Entries.Length * 64);
92 | DataOffset += 2048 - (DataOffset % 2048);
93 | var offset = DataOffset;
94 | for(var i = 0; i < EntryTable.Entries.Length; i++) {
95 | if(EntryTable.Entries[i].Size <= 0)
96 | continue;
97 | var tmp = new byte[EntryTable.Entries[i].Size];
98 | Buffer.BlockCopy(data, offset, tmp, 0, tmp.Length);
99 | SetImage(tmp, i);
100 | offset += tmp.Length;
101 | }
102 | }
103 |
104 | public int PaddingSize { get { return 0x800 - ((0x14 + EntryTable.Entries.Length * 0x40) % 0x800); } }
105 |
106 | public IEnumerable Padding { get { return new byte[PaddingSize]; } }
107 |
108 | public byte[] FileData {
109 | get {
110 | var ret = new List();
111 | uint offset = 0;
112 | Header.DataSize = 0;
113 | EntryTable.Flags = 0;
114 | EntryTable.ScreenshotCount = 0;
115 | for(var i = 0; i < EntryTable.Entries.Length; i++) {
116 | var entry = EntryTable.Entries[i];
117 | if(entry.Size <= 0)
118 | continue;
119 | entry.Offset = offset;
120 | offset += entry.Size;
121 | Header.DataSize += entry.Size;
122 | EntryTable.Flags |= (uint)(1 << i);
123 | if(i <= (int)AssetType.ScreenshotEnd && i >= (int)AssetType.ScreenshotStart)
124 | EntryTable.ScreenshotCount++;
125 | }
126 | ret.AddRange(BitConverter.GetBytes(Swap(Header.Magic)));
127 | ret.AddRange(BitConverter.GetBytes(Swap(Header.Version)));
128 | ret.AddRange(BitConverter.GetBytes(Swap(Header.DataSize)));
129 | ret.AddRange(BitConverter.GetBytes(Swap(EntryTable.Flags)));
130 | ret.AddRange(BitConverter.GetBytes(Swap(EntryTable.ScreenshotCount)));
131 | foreach(var entry in EntryTable.Entries) {
132 | ret.AddRange(BitConverter.GetBytes(Swap(entry.Offset)));
133 | ret.AddRange(BitConverter.GetBytes(Swap(entry.Size)));
134 | ret.AddRange(BitConverter.GetBytes(Swap(entry.ExtendedInfo)));
135 | ret.AddRange(entry.TextureHeader);
136 | }
137 | ret.AddRange(Padding);
138 | foreach(var entry in EntryTable.Entries.Where(entry => entry.Size > 0))
139 | ret.AddRange(entry.VideoData);
140 | return ret.ToArray();
141 | }
142 | }
143 |
144 | public bool HasBoxArt { get { return EntryTable.Entries[(int)AssetType.Boxart].Size > 0; } }
145 |
146 | public bool HasBackground { get { return EntryTable.Entries[(int)AssetType.Background].Size > 0; } }
147 |
148 | public bool HasScreenshots { get { return EntryTable.ScreenshotCount > 0; } }
149 |
150 | public bool HasIconBanner { get { return EntryTable.Entries[(int)AssetType.Icon].Size > 0 || EntryTable.Entries[(int)AssetType.Banner].Size > 0; } }
151 |
152 | private bool SetImage(Image img, int index, bool useCompression) {
153 | if(index > (int)AssetType.Max)
154 | return false;
155 | if(img == null) {
156 | EntryTable.Entries[index].ImageData = null;
157 | EntryTable.Entries[index].VideoData = new byte[0];
158 | EntryTable.Entries[index].TextureHeader = new byte[EntryTable.Entries[index].TextureHeader.Length];
159 | return true;
160 | }
161 | EntryTable.Entries[index].ImageData = img;
162 | var data = ImageToRawArgb(img);
163 | byte[] video = new byte[0], header = new byte[0];
164 | if(!AuroraAssetDll.ProcessImageToAsset(ref data, img.Width, img.Height, useCompression, ref header, ref video))
165 | return false;
166 | EntryTable.Entries[index].VideoData = video;
167 | EntryTable.Entries[index].TextureHeader = header;
168 | return true;
169 | }
170 |
171 | private void SetImage(byte[] videoData, int index) {
172 | var imageData = new byte[0];
173 | int imageWidth, imageHeight;
174 | if(!AuroraAssetDll.ProcessAssetToImage(ref EntryTable.Entries[index].TextureHeader, ref videoData, ref imageData, out imageWidth, out imageHeight))
175 | return;
176 | EntryTable.Entries[index].VideoData = videoData;
177 | EntryTable.Entries[index].ImageData = RawArgbToImage(imageData, imageWidth, imageHeight);
178 | }
179 |
180 | private void SetImage(AssetFile asset, int index) {
181 | var target = EntryTable.Entries[index];
182 | var src = asset.EntryTable.Entries[index];
183 | target.TextureHeader = src.TextureHeader;
184 | target.VideoData = src.VideoData;
185 | target.ImageData = src.ImageData;
186 | }
187 |
188 | public bool SetIcon(Image img, bool useCompression) { return SetImage(img, (int)AssetType.Icon, useCompression); }
189 |
190 | public bool SetBackground(Image img, bool useCompression) { return SetImage(img, (int)AssetType.Background, useCompression); }
191 |
192 | public bool SetBanner(Image img, bool useCompression) { return SetImage(img, (int)AssetType.Banner, useCompression); }
193 |
194 | public bool SetBoxart(Image img, bool useCompression) { return SetImage(img, (int)AssetType.Boxart, useCompression); }
195 |
196 | public bool SetScreenshot(Image img, int num, bool useCompression) {
197 | num += (int)AssetType.ScreenshotStart - 1;
198 | return num <= (int)AssetType.ScreenshotEnd && SetImage(img, num, useCompression);
199 | }
200 |
201 | public Image GetIcon() { return EntryTable.Entries[(int)AssetType.Icon].Size > 0 ? EntryTable.Entries[(int)AssetType.Icon].ImageData : null; }
202 |
203 | public Image GetBoxart() { return EntryTable.Entries[(int)AssetType.Boxart].Size > 0 ? EntryTable.Entries[(int)AssetType.Boxart].ImageData : null; }
204 |
205 | public Image GetBanner() { return EntryTable.Entries[(int)AssetType.Banner].Size > 0 ? EntryTable.Entries[(int)AssetType.Banner].ImageData : null; }
206 |
207 | public Image GetBackground() { return EntryTable.Entries[(int)AssetType.Background].Size > 0 ? EntryTable.Entries[(int)AssetType.Background].ImageData : null; }
208 |
209 | public Image GetScreenshot(int num) {
210 | num += (int)AssetType.ScreenshotStart - 1;
211 | if(num > (int)AssetType.ScreenshotEnd)
212 | return null;
213 | return EntryTable.Entries[num].Size > 0 ? EntryTable.Entries[num].ImageData : null;
214 | }
215 |
216 | public Image[] GetScreenshots() {
217 | var ret = new List();
218 | for(var i = 0; i < ScreenShotMax; i++)
219 | ret.Add(GetScreenshot(i + 1));
220 | return ret.ToArray();
221 | }
222 |
223 | public void SetBoxart(AssetFile asset) { SetImage(asset, (int)AssetType.Boxart); }
224 |
225 | public void SetBackground(AssetFile asset) { SetImage(asset, (int)AssetType.Background); }
226 |
227 | public void SetIcon(AssetFile asset) { SetImage(asset, (int)AssetType.Icon); }
228 |
229 | public void SetBanner(AssetFile asset) { SetImage(asset, (int)AssetType.Banner); }
230 |
231 | public void SetScreenshots(AssetFile asset) {
232 | for(var i = (int)AssetType.ScreenshotStart; i < (int)AssetType.ScreenshotEnd; i++)
233 | SetImage(asset, i);
234 | }
235 | }
236 |
237 | public class AssetPackEntry {
238 | public Image ImageData;
239 | public byte[] TextureHeader;
240 | public byte[] VideoData;
241 |
242 | public AssetPackEntry() {
243 | Offset = 0;
244 | VideoData = new byte[0];
245 | TextureHeader = new byte[52];
246 | }
247 |
248 | public AssetPackEntry(byte[] data, int offset) {
249 | Offset = Swap(BitConverter.ToUInt32(data, offset));
250 | VideoData = new byte[Swap(BitConverter.ToUInt32(data, offset + 4))];
251 | TextureHeader = new byte[52];
252 | Buffer.BlockCopy(data, (offset + 12), TextureHeader, 0, TextureHeader.Length);
253 | }
254 |
255 | public uint Offset { get; internal set; }
256 |
257 | public uint Size { get { return (uint)VideoData.Length; } }
258 |
259 | public uint ExtendedInfo { get { return 0; } }
260 |
261 | public Size ImageSize { get { return ImageData.Size; } }
262 |
263 | public override string ToString() {
264 | var sz = ImageSize;
265 | return string.Format("Offset: 0x{0:X}{4}Size: 0x{1:X}{4}Extended Info: 0x{2:X}{4}Texture Header Size: 0x{3:X}{4}Width: {5}{4}Height: {6}", Offset, Size, ExtendedInfo,
266 | TextureHeader.Length, Environment.NewLine, sz.Width, sz.Height);
267 | }
268 | }
269 |
270 | public class AssetPackEntryTable {
271 | public readonly AssetPackEntry[] Entries = new AssetPackEntry[(int)AssetType.Max];
272 |
273 | public AssetPackEntryTable() {
274 | Flags = 0;
275 | ScreenshotCount = 0;
276 | for(var i = 0; i < Entries.Length; i++)
277 | Entries[i] = new AssetPackEntry();
278 | }
279 |
280 | public AssetPackEntryTable(byte[] data, int offset) {
281 | Flags = Swap(BitConverter.ToUInt32(data, offset));
282 | ScreenshotCount = Swap(BitConverter.ToUInt32(data, offset + 4));
283 | offset += 8;
284 | for(var i = 0; i < Entries.Length; i++, offset += 64)
285 | Entries[i] = new AssetPackEntry(data, offset);
286 | }
287 |
288 | public uint Flags { get; internal set; }
289 |
290 | public uint ScreenshotCount { get; internal set; }
291 |
292 | public override string ToString() {
293 | var sb = new StringBuilder();
294 | sb.AppendFormat("Flags: 0x{0:X}{1}", Flags, Environment.NewLine);
295 | sb.AppendFormat("ScreenshotCount: {0}{1}", ScreenshotCount, Environment.NewLine);
296 | sb.AppendLine("Entries:");
297 | for(var i = 0; i < Entries.Length; i++) {
298 | if(i < (int)AssetType.ScreenshotStart || i > (int)AssetType.ScreenshotEnd)
299 | sb.AppendLine(((AssetType)i) + ":");
300 | else
301 | sb.AppendLine(string.Format("ScreenShot {0}:", i - (int)AssetType.ScreenshotStart));
302 | sb.AppendLine(Entries[i].Size > 0 ? Entries[i].ToString() : "No data...");
303 | }
304 | return sb.ToString();
305 | }
306 | }
307 |
308 | public class AssetPackHeader {
309 | public AssetPackHeader(uint magic, uint version, uint dataSize) {
310 | Magic = magic;
311 | Version = version;
312 | DataSize = dataSize;
313 | }
314 |
315 | public uint Magic { get; private set; }
316 |
317 | public uint Version { get; private set; }
318 |
319 | public uint DataSize { get; internal set; }
320 |
321 | public override string ToString() {
322 | return string.Format("Magic: {0}{3}Version: {1}{3}DataSize: {2}", Encoding.ASCII.GetString(BitConverter.GetBytes(Swap(Magic))), Version, DataSize, Environment.NewLine);
323 | }
324 | }
325 | }
326 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/AuroraAssetDll.cs:
--------------------------------------------------------------------------------
1 | /// ************************************************************************************
2 | /// ** File: AuroraAssetDll.cs
3 | /// ** Author: MaesterRowen (Phoenix) - May, 2015
4 | /// ** Description: Wrapper class for interfacing with the AuroraAsset.dll in C#
5 | /// ************************************************************************************
6 |
7 | using System;
8 | using System.Runtime.InteropServices;
9 |
10 | namespace PhoenixTools
11 | {
12 | class AuroraAssetDll
13 | {
14 | [DllImport("AuroraAsset.dll", CallingConvention = CallingConvention.Cdecl)]
15 | private static extern int ConvertImageToAsset(IntPtr imageData, int imageDataLen, int imageWidth, int imageHeight, int useCompression,
16 | IntPtr headerData, out int headerDataLen, IntPtr videoData, out int videoDataLen);
17 |
18 | [DllImport("AuroraAsset.dll", CallingConvention = CallingConvention.Cdecl)]
19 | private static extern int ConvertAssetToImage(IntPtr headerData, int headerDataLen, IntPtr videoData, int videoDataLen, IntPtr imageData, out int imageDataLen,
20 | out int imageWidth, out int imageHeight );
21 |
22 | [DllImport("AuroraAsset.dll", CallingConvention = CallingConvention.Cdecl)]
23 | private static extern int ConvertDDSToImage(IntPtr ddsData, int ddsDataLen, IntPtr imageData, out int imageDataLen, out int imageWidth, out int imageHeight);
24 |
25 | ///
26 | /// Takes raw pixel data in linear ARGB format and outputs Aurora .asset formatted header and video data
27 | ///
28 | ///
29 | ///
30 | ///
31 | ///
32 | ///
33 | ///
34 | ///
35 | public static bool ProcessImageToAsset(ref byte[] pixelData, int imageWidth, int imageHeight, bool useCompression, ref byte[] headerData, ref byte[] videoData)
36 | {
37 | IntPtr hd = IntPtr.Zero, vd = IntPtr.Zero, pd = IntPtr.Zero;
38 | try
39 | {
40 | bool status = false;
41 |
42 | // Store our pixel data array size for later use
43 | int pixelDataLen = pixelData.Length;
44 | if (pixelData == null || pixelDataLen == 0)
45 | {
46 | return false;
47 | }
48 |
49 | // Copy the pixel data to an unmanaged memory buffer
50 | pd = Marshal.AllocHGlobal(pixelDataLen);
51 | Marshal.Copy(pixelData, 0, pd, pixelDataLen);
52 |
53 | // Create variables to hold buffer sizes
54 | int headerDataLen;
55 | int videoDataLen;
56 | int result = ConvertImageToAsset(pd, pixelDataLen, imageWidth, imageHeight, useCompression ? 1 : 0, IntPtr.Zero, out headerDataLen, IntPtr.Zero, out videoDataLen);
57 | if (result == 1)
58 | {
59 | // Allocate unmanaged memory for asset data
60 | hd = Marshal.AllocHGlobal(headerDataLen);
61 | vd = Marshal.AllocHGlobal(videoDataLen);
62 |
63 | // Obtain data
64 | result = ConvertImageToAsset(pd, pixelDataLen, imageWidth, imageHeight, useCompression ? 1 : 0, hd, out headerDataLen, vd, out videoDataLen);
65 | if (result == 1)
66 | {
67 | // Copy our header data
68 | headerData = new byte[headerDataLen];
69 | Marshal.Copy(hd, headerData, 0, headerDataLen);
70 |
71 | // Copy our video data
72 | videoData = new byte[videoDataLen];
73 | Marshal.Copy(vd, videoData, 0, videoDataLen);
74 |
75 | status = true;
76 | }
77 | }
78 | return status;
79 | }
80 | catch( Exception e)
81 | {
82 | Console.WriteLine(e.Message);
83 | }
84 | finally {
85 | // Clean up unmanaged file data
86 | if (pd != IntPtr.Zero)
87 | Marshal.FreeHGlobal(pd);
88 | if (hd != IntPtr.Zero)
89 | Marshal.FreeHGlobal(hd);
90 | if (vd != IntPtr.Zero)
91 | Marshal.FreeHGlobal(vd);
92 | }
93 |
94 | return false;
95 | }
96 |
97 | ///
98 | /// Takes asset header and video data and outputs raw pixel data in BGRA format.
99 | ///
100 | ///
101 | ///
102 | ///
103 | ///
104 | ///
105 | ///
106 | public static bool ProcessAssetToImage(ref byte[] headerData, ref byte[] videoData, ref byte[] pixelData, out int imageWidth, out int imageHeight )
107 | {
108 | IntPtr hd = IntPtr.Zero, vd = IntPtr.Zero, pd = IntPtr.Zero;
109 | try
110 | {
111 | bool status = false;
112 |
113 | int headerDataLen = headerData.Length;
114 | int videoDataLen = videoData.Length;
115 | if (headerDataLen == 0 || videoDataLen == 0)
116 | {
117 | imageWidth = 0;
118 | imageHeight = 0;
119 | return false;
120 | }
121 |
122 | // Copy the pixel data to an unmanaged memory buffer
123 | hd = Marshal.AllocHGlobal(headerDataLen);
124 | Marshal.Copy(headerData, 0, hd, headerDataLen);
125 |
126 | vd = Marshal.AllocHGlobal(videoDataLen);
127 | Marshal.Copy(videoData, 0, vd, videoDataLen);
128 |
129 | // Create variables to hold buffer sizes
130 | int imageDataLen;
131 | int result = ConvertAssetToImage( hd, headerDataLen, vd, videoDataLen, IntPtr.Zero, out imageDataLen, out imageWidth, out imageHeight );
132 | if (result == 1)
133 | {
134 | // Allocate unmanaged memory for asset data
135 | pd = Marshal.AllocHGlobal(imageDataLen);
136 |
137 | // Obtain data
138 | result = ConvertAssetToImage(hd, headerDataLen, vd, videoDataLen, pd, out imageDataLen, out imageWidth, out imageHeight);
139 | if (result == 1)
140 | {
141 | // Copy our pixel data
142 | pixelData = new byte[imageDataLen];
143 | Marshal.Copy(pd, pixelData, 0, imageDataLen);
144 | status = true;
145 | }
146 | }
147 | return status;
148 | }
149 | catch (Exception e)
150 | {
151 | Console.WriteLine(e.Message);
152 | }
153 | finally
154 | {
155 | // Clean up unmanaged file data
156 | if (pd != IntPtr.Zero)
157 | Marshal.FreeHGlobal(pd);
158 | if (hd != IntPtr.Zero)
159 | Marshal.FreeHGlobal(hd);
160 | if (vd != IntPtr.Zero)
161 | Marshal.FreeHGlobal(vd);
162 | }
163 |
164 | imageWidth = 0;
165 | imageHeight = 0;
166 | return false;
167 | }
168 |
169 | ///
170 | /// Takes a DDS image data (with header) and outputs raw pixel data in BGRA format.
171 | ///
172 | ///
173 | ///
174 | ///
175 | ///
176 | ///
177 | public static bool ProcessDDSToImage(ref byte[] ddsData, ref byte[] pixelData, out int imageWidth, out int imageHeight)
178 | {
179 | IntPtr dds = IntPtr.Zero, pd = IntPtr.Zero;
180 | try
181 | {
182 | bool status = false;
183 |
184 | int ddsDataLen = ddsData.Length;
185 | if (ddsDataLen == 0)
186 | {
187 | imageWidth = 0;
188 | imageHeight = 0;
189 | return false;
190 | }
191 |
192 | // Copy the pixel data to an unmanaged memory buffer
193 | dds = Marshal.AllocHGlobal(ddsDataLen);
194 | Marshal.Copy(ddsData, 0, dds, ddsDataLen);
195 |
196 | // Create variables to hold buffer sizes
197 | int imageDataLen;
198 | int result = ConvertDDSToImage(dds, ddsDataLen, IntPtr.Zero, out imageDataLen, out imageWidth, out imageHeight);
199 | if (result == 1)
200 | {
201 | // Allocate unmanaged memory for asset data
202 | pd = Marshal.AllocHGlobal(imageDataLen);
203 |
204 | // Obtain data
205 | result = ConvertDDSToImage(dds, ddsDataLen, pd, out imageDataLen, out imageWidth, out imageHeight);
206 | if (result == 1)
207 | {
208 | // Copy our pixel data
209 | pixelData = new byte[imageDataLen];
210 | Marshal.Copy(pd, pixelData, 0, imageDataLen);
211 | status = true;
212 | }
213 | }
214 | return status;
215 | }
216 | catch (Exception e)
217 | {
218 | Console.WriteLine(e.Message);
219 | }
220 | finally
221 | {
222 | // Clean up unmanaged file data
223 | if (pd != IntPtr.Zero)
224 | Marshal.FreeHGlobal(pd);
225 | if (dds != IntPtr.Zero)
226 | Marshal.FreeHGlobal(dds);
227 | }
228 |
229 | imageWidth = 0;
230 | imageHeight = 0;
231 | return false;
232 | }
233 | }
234 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/AuroraDbManager.cs:
--------------------------------------------------------------------------------
1 | //
2 | // AuroraDbManager.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 14/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Data;
12 | using System.Data.SQLite;
13 | using System.Globalization;
14 | using System.IO;
15 | using System.Linq;
16 | using System.Threading;
17 |
18 | internal static class AuroraDbManager {
19 | private static SQLiteConnection _content;
20 |
21 | private static void ConnectToContent(string path) {
22 | if(_content != null)
23 | _content.Close();
24 | _content = new SQLiteConnection("Data Source=\"" + path + "\";Version=3;");
25 | _content.Open();
26 | }
27 |
28 | private static DataTable GetContentDataTable(string sql) {
29 | var dt = new DataTable();
30 | try {
31 | var cmd = new SQLiteCommand(sql, _content);
32 | using(var reader = cmd.ExecuteReader())
33 | dt.Load(reader);
34 | }
35 | catch(Exception ex) {
36 | MainWindow.SaveError(ex);
37 | }
38 | return dt;
39 | }
40 |
41 | public static IEnumerable GetDbTitles(string path) {
42 | ConnectToContent(path);
43 | var ret = GetContentItems().Select(item => item).ToList();
44 | _content.Close();
45 | GC.Collect();
46 | while(true) {
47 | try {
48 | File.Delete(path);
49 | break;
50 | }
51 | catch(IOException) {
52 | Thread.Sleep(100);
53 | }
54 | }
55 | return ret;
56 | }
57 |
58 | private static IEnumerable GetContentItems() { return GetContentDataTable("SELECT * FROM ContentItems").Select().Select(row => new ContentItem(row)).ToArray(); }
59 |
60 | internal class ContentItem {
61 | public ContentItem(DataRow row) {
62 | DatabaseId = ((int)((long)row["Id"])).ToString("X08");
63 | TitleId = ((int)((long)row["TitleId"])).ToString("X08");
64 | MediaId = ((int)((long)row["MediaId"])).ToString("X08");
65 | var discNum = (int)((long)row["DiscNum"]);
66 | if(discNum <= 0)
67 | discNum = 1;
68 | DiscNum = discNum.ToString(CultureInfo.InvariantCulture);
69 | TitleName = (string)row["TitleName"];
70 | }
71 |
72 | public string TitleId { get; private set; }
73 |
74 | public string MediaId { get; private set; }
75 |
76 | public string DiscNum { get; private set; }
77 |
78 | public string TitleName { get; private set; }
79 |
80 | public string DatabaseId { get; private set; }
81 |
82 | public string Path { get { return string.Format("{0}_{1}", TitleId, DatabaseId); } }
83 |
84 | public void SaveAsBoxart(byte[] data) { App.FtpOperations.SendAssetData(string.Format("GC{0}.asset", TitleId), Path, data); }
85 |
86 | public void SaveAsBackground(byte[] data) { App.FtpOperations.SendAssetData(string.Format("BK{0}.asset", TitleId), Path, data); }
87 |
88 | public void SaveAsIconBanner(byte[] data) { App.FtpOperations.SendAssetData(string.Format("GL{0}.asset", TitleId), Path, data); }
89 |
90 | public void SaveAsScreenshots(byte[] data) { App.FtpOperations.SendAssetData(string.Format("SS{0}.asset", TitleId), Path, data); }
91 |
92 | public byte[] GetBoxart() { return App.FtpOperations.GetAssetData(string.Format("GC{0}.asset", TitleId), Path); }
93 |
94 | public byte[] GetBackground() { return App.FtpOperations.GetAssetData(string.Format("BK{0}.asset", TitleId), Path); }
95 |
96 | public byte[] GetIconBanner() { return App.FtpOperations.GetAssetData(string.Format("GL{0}.asset", TitleId), Path); }
97 |
98 | public byte[] GetScreenshots() { return App.FtpOperations.GetAssetData(string.Format("SS{0}.asset", TitleId), Path); }
99 | }
100 | }
101 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/FSDAsset.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FSDAsset.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Drawing;
11 | using System.Drawing.Imaging;
12 | using System.IO;
13 | using System.Linq;
14 | using System.Runtime.InteropServices;
15 | using PhoenixTools;
16 |
17 | internal class FsdAsset {
18 | public enum FsdAssetType: uint {
19 | Thumbnail = 0x01,
20 | Background = 0x02,
21 | Banner = 0x04,
22 | Boxart = 0x08,
23 | Preview = 0x10,
24 | Screenshot = 0x20,
25 | Slot = 0x40,
26 | FullCover = 0x80
27 | }
28 |
29 | public readonly uint AssetCount;
30 | public readonly uint AssetFlags;
31 | public readonly FsdAssetEntry[] Entries = new FsdAssetEntry[10];
32 | public readonly uint Magic;
33 | public readonly uint Reserved;
34 | public readonly uint ScreenshotCount;
35 | public readonly FsdScreenshotEntry[] Screenshots = new FsdScreenshotEntry[20];
36 | public readonly uint Version;
37 |
38 | public FsdAsset(byte[] data) {
39 | if(data.Length < 0x1F8)
40 | throw new Exception("Invalid file size");
41 | Magic = Swap(BitConverter.ToUInt32(data, 0));
42 | if(Magic != 0x46534441)
43 | throw new Exception("Invalid asset file magic!");
44 | Version = Swap(BitConverter.ToUInt32(data, 4));
45 | if(Version != 1)
46 | throw new NotSupportedException("Unsupported asset file version!");
47 | Reserved = Swap(BitConverter.ToUInt32(data, 8));
48 | AssetFlags = Swap(BitConverter.ToUInt32(data, 12));
49 | AssetCount = Swap(BitConverter.ToUInt32(data, 16));
50 | ScreenshotCount = Swap(BitConverter.ToUInt32(data, 20));
51 | for(var i = 0; i < Entries.Length; i++)
52 | Entries[i] = new FsdAssetEntry(ref data, 24 + (i * 16));
53 | for(var i = 0; i < Screenshots.Length; i++)
54 | Screenshots[i] = new FsdScreenshotEntry(ref data, (24 + (16 * Entries.Length)) + (i * 16));
55 | }
56 |
57 | private static uint Swap(uint x) { return (x & 0x000000FF) << 24 | (x & 0x0000FF00) << 8 | (x & 0x00FF0000) >> 8 | (x & 0xFF000000) >> 24; }
58 |
59 | private static Image RawArgbToImage(byte[] raw, int width, int height) {
60 | var ret = new Bitmap(width, height);
61 | var rect = new Rectangle(new Point(0, 0), new Size(width, height));
62 | var bmpData = ret.LockBits(rect, ImageLockMode.ReadWrite, ret.PixelFormat);
63 | Marshal.Copy(raw, 0, bmpData.Scan0, raw.Length);
64 | ret.UnlockBits(bmpData);
65 | return ret;
66 | }
67 |
68 | private static Image GetImage(byte[] data) {
69 | if(data[0] == 'D' || data[1] == 'D' || data[1] == 'S') {
70 | var imageData = new byte[0];
71 | int imageWidth, imageHeight;
72 | AuroraAssetDll.ProcessDDSToImage(ref data, ref imageData, out imageWidth, out imageHeight);
73 | return RawArgbToImage(imageData, imageWidth, imageHeight);
74 | }
75 | using(var ms = new MemoryStream(data)) {
76 | var img = Image.FromStream(ms);
77 | var img2 = new Bitmap(img.Width, img.Height, PixelFormat.Format32bppArgb);
78 | var g = Graphics.FromImage(img);
79 | g.DrawImage(img2, new Point(0, 0));
80 | g.Dispose();
81 | img.Dispose();
82 | return img2;
83 | }
84 | }
85 |
86 | public Image GetBanner() { return (from entry in Entries where entry.AssetType == FsdAssetType.Banner && entry.Size > 0 select GetImage(entry.Data)).FirstOrDefault(); }
87 |
88 | public Image GetBackground() { return (from entry in Entries where entry.AssetType == FsdAssetType.Background && entry.Size > 0 select GetImage(entry.Data)).FirstOrDefault(); }
89 |
90 | public Image GetIcon() { return (from entry in Entries where entry.AssetType == FsdAssetType.Thumbnail && entry.Size > 0 select GetImage(entry.Data)).FirstOrDefault(); }
91 |
92 | public Image GetBoxart() { return (from entry in Entries where entry.AssetType == FsdAssetType.FullCover && entry.Size > 0 select GetImage(entry.Data)).FirstOrDefault(); }
93 |
94 | public Image[] GetScreenshots() {
95 | var ret = (from entry in Screenshots where entry.Size > 0 select GetImage(entry.Data)).ToList();
96 | if(ret.Count == 0)
97 | ret.AddRange(from entry in Entries where entry.AssetType == FsdAssetType.Screenshot && entry.Size > 0 select GetImage(entry.Data));
98 | return ret.ToArray();
99 | }
100 |
101 | public class FsdAssetEntry {
102 | public readonly FsdAssetType AssetType;
103 | public readonly byte[] Data;
104 | public readonly uint Offset;
105 | public readonly uint Size;
106 | public readonly uint TotalSize;
107 |
108 | public FsdAssetEntry(ref byte[] data, int offset) {
109 | AssetType = (FsdAssetType)Swap(BitConverter.ToUInt32(data, offset));
110 | Offset = Swap(BitConverter.ToUInt32(data, offset + 4));
111 | Size = Swap(BitConverter.ToUInt32(data, offset + 8));
112 | TotalSize = Swap(BitConverter.ToUInt32(data, offset + 12));
113 | if(Size <= 0)
114 | return;
115 | Data = new byte[Size];
116 | Buffer.BlockCopy(data, (int)Offset, Data, 0, Data.Length);
117 | }
118 | }
119 |
120 | public class FsdScreenshotEntry {
121 | public readonly byte[] Data;
122 | public readonly uint Offset;
123 | public readonly uint Reserved;
124 | public readonly uint Size;
125 | public readonly uint TotalSize;
126 |
127 | public FsdScreenshotEntry(ref byte[] data, int offset) {
128 | Offset = Swap(BitConverter.ToUInt32(data, offset));
129 | Size = Swap(BitConverter.ToUInt32(data, offset + 4));
130 | TotalSize = Swap(BitConverter.ToUInt32(data, offset + 8));
131 | Reserved = Swap(BitConverter.ToUInt32(data, offset + 12));
132 | if(Size <= 0)
133 | return;
134 | Data = new byte[Size];
135 | Buffer.BlockCopy(data, (int)Offset, Data, 0, Data.Length);
136 | }
137 | }
138 | }
139 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/FTPOperations.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FTPOperations.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Globalization;
11 | using System.IO;
12 | using System.Linq;
13 | using System.Net;
14 | using System.Net.FtpClient;
15 | using System.Runtime.Serialization;
16 | using System.Runtime.Serialization.Json;
17 |
18 | internal class FtpOperations {
19 | private readonly DataContractJsonSerializer _serializer = new DataContractJsonSerializer(typeof(FtpSettings));
20 | public EventHandler StatusChanged;
21 | private FtpClient _client;
22 | private FtpSettings _settings;
23 |
24 | public FtpOperations() { LoadSettings(); }
25 |
26 | public string IpAddress { get { return _settings.IpAddress; } }
27 |
28 | public string Username { get { return _settings.Username; } }
29 |
30 | public string Password { get { return _settings.Password; } }
31 |
32 | public bool HaveSettings { get { return _settings.Loaded; } }
33 |
34 | public bool ConnectionEstablished {
35 | get {
36 | if(_client != null && _client.IsConnected)
37 | return _client.IsConnected;
38 | try { MakeConnection(); }
39 | catch {}
40 | return _client != null && _client.IsConnected;
41 | }
42 | }
43 |
44 | public string Port { get { return _settings.Port; } }
45 |
46 | private void SendStatusChanged(string msg, params object[] param) {
47 | var handler = StatusChanged;
48 | if(handler != null)
49 | handler.Invoke(this, new StatusArgs(string.Format(msg, param)));
50 | }
51 |
52 | private void LoadSettings() {
53 | var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
54 | try {
55 | path = !string.IsNullOrWhiteSpace(path) ? Path.Combine(path, "AuroraAssetEditor", "ftp.json") : "ftp.json";
56 | using(var stream = File.OpenRead(path))
57 | _settings = (FtpSettings)_serializer.ReadObject(stream);
58 | _settings.Loaded = true;
59 | }
60 | catch {
61 | _settings = new FtpSettings {
62 | Loaded = false
63 | };
64 | }
65 | }
66 |
67 | public bool TestConnection(string ip, string user, string pass, string port) {
68 | _settings.IpAddress = ip;
69 | _settings.Username = user;
70 | _settings.Password = pass;
71 | _settings.Port = port;
72 | _settings.Loaded = true;
73 | try {
74 | if(MakeConnection())
75 | return true;
76 | SendStatusChanged("Connection test Failed...");
77 | return false;
78 | }
79 | catch(Exception ex) {
80 | SendStatusChanged("Error: {0}", ex.Message);
81 | return false;
82 | }
83 | }
84 |
85 | private bool MakeConnection() {
86 | _client = new FtpClient {
87 | Credentials = new NetworkCredential(_settings.Username, _settings.Password),
88 | Host = _settings.IpAddress,
89 | SocketKeepAlive = true
90 | };
91 | int port;
92 | if(!int.TryParse(_settings.Port, out port)) {
93 | port = 21;
94 | _settings.Port = port.ToString(CultureInfo.InvariantCulture);
95 | }
96 | _client.Port = port;
97 | SendStatusChanged("Connecting to {0}...", _settings.IpAddress);
98 | _client.Connect();
99 | if(!_client.IsConnected)
100 | return false;
101 | SendStatusChanged("Connection to {0} Established checking for Aurora...", _settings.IpAddress);
102 | var reply = _client.Execute("SITE REVISION");
103 | if(!reply.Success)
104 | return false;
105 | SendStatusChanged("Connection to Aurora revision {0} Established...", reply.Message);
106 | return true;
107 | }
108 |
109 | public void SaveSettings() {
110 | var path = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
111 | path = !string.IsNullOrWhiteSpace(path) ? Path.Combine(path, "AuroraAssetEditor", "ftp.json") : "ftp.json";
112 | path = Path.GetFullPath(path);
113 | if (File.Exists(path))
114 | File.Delete(path);
115 | var dir = Path.GetDirectoryName(path);
116 | if(string.IsNullOrWhiteSpace(dir))
117 | return;
118 | if(!Directory.Exists(dir))
119 | Directory.CreateDirectory(dir);
120 | using(var stream = File.OpenWrite(path))
121 | _serializer.WriteObject(stream, _settings);
122 | }
123 |
124 | public void SaveSettings(string ip, string user, string pass, string port) {
125 | _settings.IpAddress = ip;
126 | _settings.Username = user;
127 | _settings.Password = pass;
128 | _settings.Port = port;
129 | _settings.Loaded = true;
130 | SaveSettings();
131 | }
132 |
133 | public bool NavigateToGameDataDir() {
134 | if(_client == null || !_client.IsConnected) {
135 | if(!MakeConnection()) {
136 | SendStatusChanged("Connection failed to {0}", _settings.IpAddress);
137 | return false; // Not connected
138 | }
139 | }
140 | if(_client == null)
141 | return false;
142 | const string dir = "/Game/Data/GameData/";
143 | SendStatusChanged("Changing working directory to {0}...", dir);
144 | _client.SetWorkingDirectory(dir);
145 | return _client.GetWorkingDirectory().Equals(dir, StringComparison.InvariantCultureIgnoreCase);
146 | }
147 |
148 | public bool NavigateToAssetDir(string assetName) {
149 | if(!NavigateToGameDataDir())
150 | return false;
151 | var dir = "/Game/Data/GameData/" + assetName + "/";
152 | SendStatusChanged("Changing working directory to {0}...", dir);
153 | _client.SetWorkingDirectory(dir);
154 | return _client.GetWorkingDirectory().Equals(dir, StringComparison.InvariantCultureIgnoreCase);
155 | }
156 |
157 | public string[] GetDirList() { return (from item in _client.GetListing() where item.Type == FtpFileSystemObjectType.Directory select item.Name).ToArray(); }
158 |
159 | public byte[] GetAssetData(string file, string assetDir) {
160 | if(!NavigateToAssetDir(assetDir))
161 | return null;
162 | var size = (from item in _client.GetListing() where item.Name.Equals(file, StringComparison.InvariantCultureIgnoreCase) select (int)item.Size).FirstOrDefault();
163 | if(size <= 0)
164 | return null;
165 | var data = new byte[size];
166 | var offset = 0;
167 | using(var stream = _client.OpenRead(file)) {
168 | while(offset < data.Length)
169 | offset += stream.Read(data, offset, data.Length - offset);
170 | }
171 | return data;
172 | }
173 |
174 | public bool SendAssetData(string file, string assetDir, byte[] data) {
175 | if(!NavigateToAssetDir(assetDir))
176 | return false;
177 | using(var stream = _client.OpenWrite(file))
178 | stream.Write(data, 0, data.Length);
179 | return true;
180 | }
181 |
182 | public bool DownloadContentDb(string path) {
183 | if(_client == null || !_client.IsConnected) {
184 | if(!MakeConnection()) {
185 | SendStatusChanged("Connection failed to {0}", _settings.IpAddress);
186 | return false; // Not connected
187 | }
188 | }
189 | if(_client == null)
190 | return false;
191 | const string dir = "/Game/Data/DataBases/";
192 | SendStatusChanged("Changing working directory to {0}...", dir);
193 | _client.SetWorkingDirectory(dir);
194 | if(!_client.GetWorkingDirectory().Equals(dir, StringComparison.InvariantCultureIgnoreCase))
195 | return false;
196 | var size = (from item in _client.GetListing() where item.Name.Equals("Content.db", StringComparison.InvariantCultureIgnoreCase) select (int)item.Size).FirstOrDefault();
197 | if(size <= 0)
198 | return false;
199 | var data = new byte[size];
200 | var offset = 0;
201 | using(var stream = _client.OpenRead("Content.db")) {
202 | while(offset < data.Length)
203 | offset += stream.Read(data, offset, data.Length - offset);
204 | }
205 | File.WriteAllBytes(path, data);
206 | return true;
207 | }
208 |
209 | [DataContract] private class FtpSettings {
210 | public bool Loaded;
211 |
212 | [DataMember(Name = "ip")] public string IpAddress { get; set; }
213 |
214 | [DataMember(Name = "user")] public string Username { get; set; }
215 |
216 | [DataMember(Name = "pass")] public string Password { get; set; }
217 |
218 | [DataMember(Name = "port")] public string Port { get; set; }
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/InternetArchiveAssetDownloader.cs:
--------------------------------------------------------------------------------
1 | //
2 | // InternetArchiveDownloader.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by aenrione
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes
9 | {
10 | using System;
11 | using System.Collections.Generic;
12 | using System.Drawing;
13 | using System.Text.RegularExpressions;
14 | using System.IO;
15 | using System.Net;
16 | using System.Text;
17 |
18 | internal class InternetArchiveDownloader
19 | {
20 | private const string BaseUrl = "https://archive.org/download/xboxunity-covers-fulldump_202311/xboxunity-covers-fulldump/";
21 | public static EventHandler StatusChanged;
22 |
23 | internal static void SendStatusChanged(string msg)
24 | {
25 | var handler = StatusChanged;
26 | if (handler != null)
27 | handler.Invoke(null, new StatusArgs(msg));
28 | }
29 |
30 | public InternetArchiveAsset[] GetTitleInfo(uint titleId)
31 | {
32 | string titleFolder = string.Format("{0:X08}", titleId);
33 | List assets = new List();
34 |
35 | try
36 | {
37 | using (WebClient client = new WebClient())
38 | {
39 | string folderUrl = $"{BaseUrl}{titleFolder}/";
40 | string htmlContent = client.DownloadString(folderUrl);
41 |
42 | foreach (string subDir in ParseDirectoriesFromHtmlRegex(htmlContent))
43 | {
44 | assets.Add(new InternetArchiveAsset
45 | {
46 | TitleId = titleId,
47 | MainFolder = titleFolder,
48 | SubFolder = subDir,
49 | AssetType = "Cover"
50 | });
51 | }
52 | }
53 | }
54 | catch (Exception ex)
55 | {
56 | SendStatusChanged($"Error fetching directory: {ex.Message}");
57 | }
58 |
59 | return assets.ToArray();
60 | }
61 |
62 | private IEnumerable ParseDirectoriesFromHtmlRegex(string html)
63 | {
64 | const string HREF_REGEX = "(.+)/";
65 |
66 | foreach (Match match in Regex.Matches(html, HREF_REGEX))
67 | {
68 | var hrefValue = match.Groups[1].Value;
69 | var text = match.Groups[2].Value;
70 |
71 | if (hrefValue == text)
72 | {
73 | yield return hrefValue;
74 | }
75 | }
76 | }
77 |
78 | public Image DownloadCover(InternetArchiveAsset asset)
79 | {
80 | try
81 | {
82 | string coverUrl = $"{BaseUrl}{asset.MainFolder}/{asset.SubFolder}/boxart.png";
83 | using (WebClient wc = new WebClient())
84 | {
85 | byte[] imageData = wc.DownloadData(coverUrl);
86 |
87 | if (imageData == null || imageData.Length == 0)
88 | {
89 | SendStatusChanged("No image data received");
90 | return null;
91 | }
92 |
93 | MemoryStream ms = new MemoryStream(imageData);
94 |
95 | try
96 | {
97 | using (Image originalImage = Image.FromStream(ms, true, true))
98 | {
99 | return new Bitmap(originalImage);
100 | }
101 | }
102 | finally
103 | {
104 | ms.Dispose();
105 | }
106 | }
107 | }
108 | catch (WebException webEx)
109 | {
110 | SendStatusChanged($"Network error downloading cover: {webEx.Message}");
111 | return null;
112 | }
113 | catch (ArgumentException argEx)
114 | {
115 | SendStatusChanged($"Invalid image data received: {argEx.Message}");
116 | return null;
117 | }
118 | catch (Exception ex)
119 | {
120 | SendStatusChanged($"Error downloading cover: {ex.Message}");
121 | return null;
122 | }
123 | }
124 | }
125 |
126 | // Modified asset class to handle the nested structure
127 | public class InternetArchiveAsset
128 | {
129 | private Image _cover;
130 | public uint TitleId { get; set; }
131 | public string MainFolder { get; set; } // The title ID folder
132 | public string SubFolder { get; set; } // The subfolder containing the actual cover
133 | public string AssetType { get; set; }
134 | public bool HaveAsset { get { return _cover != null; } }
135 |
136 | public Image GetCover()
137 | {
138 | if (_cover == null)
139 | {
140 | var downloader = new InternetArchiveDownloader();
141 | _cover = downloader.DownloadCover(this);
142 | }
143 | return _cover;
144 | }
145 |
146 | public override string ToString()
147 | {
148 | return $"TitleID: {TitleId:X8} - Variant: {SubFolder}";
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/StatusArgs.cs:
--------------------------------------------------------------------------------
1 | //
2 | // StatusArgs.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 |
11 | internal class StatusArgs: EventArgs {
12 | public readonly string StatusMessage;
13 |
14 | public StatusArgs(string statusMessage) { StatusMessage = statusMessage; }
15 | }
16 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/ThreadSafeObservableCollection.cs:
--------------------------------------------------------------------------------
1 | //
2 | // ThreadSafeObservableCollection.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 14/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System.Collections.ObjectModel;
10 | using System.Threading;
11 | using System.Windows.Threading;
12 |
13 | public class ThreadSafeObservableCollection : ObservableCollection {
14 | private readonly Dispatcher _dispatcher;
15 | private readonly ReaderWriterLockSlim _lock;
16 |
17 | public ThreadSafeObservableCollection() {
18 | _dispatcher = Dispatcher.CurrentDispatcher;
19 | _lock = new ReaderWriterLockSlim();
20 | }
21 |
22 | protected override void ClearItems() {
23 | _dispatcher.InvokeIfRequired(() => {
24 | _lock.EnterWriteLock();
25 | try { base.ClearItems(); }
26 | finally { _lock.ExitWriteLock(); }
27 | }, DispatcherPriority.DataBind);
28 | }
29 |
30 | protected override void InsertItem(int index, T item) {
31 | _dispatcher.InvokeIfRequired(() => {
32 | if(index > Count)
33 | return;
34 |
35 | _lock.EnterWriteLock();
36 | try { base.InsertItem(index, item); }
37 | finally { _lock.ExitWriteLock(); }
38 | }, DispatcherPriority.DataBind);
39 | }
40 |
41 | protected override void MoveItem(int oldIndex, int newIndex) {
42 | _dispatcher.InvokeIfRequired(() => {
43 | _lock.EnterReadLock();
44 | var itemCount = Count;
45 | _lock.ExitReadLock();
46 |
47 | if(oldIndex >= itemCount | newIndex >= itemCount | oldIndex == newIndex)
48 | return;
49 |
50 | _lock.EnterWriteLock();
51 | try { base.MoveItem(oldIndex, newIndex); }
52 | finally { _lock.ExitWriteLock(); }
53 | }, DispatcherPriority.DataBind);
54 | }
55 |
56 | protected override void RemoveItem(int index) {
57 | _dispatcher.InvokeIfRequired(() => {
58 | if(index >= Count)
59 | return;
60 |
61 | _lock.EnterWriteLock();
62 | try { base.RemoveItem(index); }
63 | finally { _lock.ExitWriteLock(); }
64 | }, DispatcherPriority.DataBind);
65 | }
66 |
67 | protected override void SetItem(int index, T item) {
68 | _dispatcher.InvokeIfRequired(() => {
69 | _lock.EnterWriteLock();
70 | try { base.SetItem(index, item); }
71 | finally { _lock.ExitWriteLock(); }
72 | }, DispatcherPriority.DataBind);
73 | }
74 | }
75 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/WpfControlThreadingExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace AuroraAssetEditor.Classes {
2 | using System;
3 | using System.Threading;
4 | using System.Windows.Threading;
5 |
6 | public static class WpfControlThreadingExtensions
7 | {
8 |
9 | public static void InvokeIfRequired(this Dispatcher disp, Action dotIt, DispatcherPriority priority)
10 | {
11 | if (disp.Thread != Thread.CurrentThread)
12 | disp.Invoke(priority, dotIt);
13 | else
14 | dotIt();
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/XboxAssetDownloader.cs:
--------------------------------------------------------------------------------
1 | //
2 | // XboxAssetDownloader.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Drawing;
12 | using System.Globalization;
13 | using System.IO;
14 | using System.Linq;
15 | using System.Net;
16 | using System.Runtime.Serialization;
17 | using System.Runtime.Serialization.Json;
18 | using System.Text;
19 | using System.Xml;
20 |
21 | internal class XboxAssetDownloader {
22 | public static EventHandler StatusChanged;
23 | private readonly DataContractJsonSerializer _serializer = new DataContractJsonSerializer(typeof(XboxKeywordResponse));
24 |
25 | internal static void SendStatusChanged(string msg) {
26 | var handler = StatusChanged;
27 | if(handler != null)
28 | handler.Invoke(null, new StatusArgs(msg));
29 | }
30 |
31 | public XboxTitleInfo[] GetTitleInfo(uint titleId, XboxLocale locale) {
32 | return new[] {
33 | XboxTitleInfo.FromTitleId(titleId, locale)
34 | };
35 | }
36 |
37 | public XboxTitleInfo[] GetTitleInfo(string keywords, XboxLocale locale) {
38 | var url = string.Format("http://marketplace.xbox.com/{0}/SiteSearch/xbox/?query={1}&PageSize=5", locale.Locale, WebUtility.UrlEncode(keywords));
39 | var wc = new WebClient();
40 | var ret = new List();
41 | using(var stream = wc.OpenRead(url)) {
42 | if(stream == null)
43 | return ret.ToArray();
44 | var res = (XboxKeywordResponse)_serializer.ReadObject(stream);
45 | ret.AddRange(from entry in res.Entries where entry.DetailsUrl != null let tid = entry.DetailsUrl.IndexOf("d802", StringComparison.Ordinal) where tid > 0 && entry.DetailsUrl.Length >= tid + 12 select uint.Parse(entry.DetailsUrl.Substring(tid + 4, 8), NumberStyles.HexNumber) into titleId select XboxTitleInfo.FromTitleId(titleId, locale));
46 | }
47 | return ret.ToArray();
48 | }
49 |
50 | public static XboxLocale[] GetLocales() {
51 | try {
52 | var ret = new List();
53 | ret.Add(new XboxLocale("es-AR", "Argentina - Espa\xf1ol"));
54 | ret.Add(new XboxLocale("pt-BR", "Brasil - Portugu\xeas"));
55 | ret.Add(new XboxLocale("en-CA", "Canada - English"));
56 | ret.Add(new XboxLocale("fr-CA", "Canada - Fran\xe7ais"));
57 | ret.Add(new XboxLocale("es-CL", "Chile - Espa\xf1ol"));
58 | ret.Add(new XboxLocale("es-CO", "Colombia - Espa\xf1ol"));
59 | ret.Add(new XboxLocale("es-MX", "M\xe9xico - Espa\xf1ol"));
60 | ret.Add(new XboxLocale("en-US", "United States - English"));
61 | ret.Add(new XboxLocale("nl-BE", "Belgi\xeb - Nederlands"));
62 | ret.Add(new XboxLocale("fr-BE", "Belgique - Fran\xe7ais"));
63 | ret.Add(new XboxLocale("cs-CZ", "\u010cesk\xe1 Republika - \u010ce\u0161tina"));
64 | ret.Add(new XboxLocale("da-DK", "Danmark - Dansk"));
65 | ret.Add(new XboxLocale("de-DE", "Deutschland - Deutsch"));
66 | ret.Add(new XboxLocale("es-ES", "Espa\xf1a - Espa\xf1ol"));
67 | ret.Add(new XboxLocale("fr-FR", "France - Fran\xe7ais"));
68 | ret.Add(new XboxLocale("en-IE", "Ireland - English"));
69 | ret.Add(new XboxLocale("it-IT", "Italia - Italiano"));
70 | ret.Add(new XboxLocale("hu-HU", "Magyarorsz\xe1g - Magyar"));
71 | ret.Add(new XboxLocale("nl-NL", "Nederland - Nederlands"));
72 | ret.Add(new XboxLocale("nb-NO", "Norge - Norsk bokm\xe5l"));
73 | ret.Add(new XboxLocale("de-AT", "\xd6sterreich - Deutsch"));
74 | ret.Add(new XboxLocale("pl-PL", "Polska - Polski"));
75 | ret.Add(new XboxLocale("pt-PT", "Portugal - Portugu\xeas"));
76 | ret.Add(new XboxLocale("de-CH", "Schweiz - Deutsch"));
77 | ret.Add(new XboxLocale("sk-SK", "Slovensko - Sloven\u010dina"));
78 | ret.Add(new XboxLocale("fr-CH", "Suisse - Fran\xe7ais"));
79 | ret.Add(new XboxLocale("fi-FI", "Suomi - Suomi"));
80 | ret.Add(new XboxLocale("sv-SE", "Sverige - Svenska"));
81 | ret.Add(new XboxLocale("en-GB", "United Kingdom - English"));
82 | ret.Add(new XboxLocale("el-GR", "\u0395\u03bb\u03bb\u03ac\u03b4\u03b1 - \u0395\u03bb\u03bb\u03b7\u03bd\u03b9\u03ba\u03ac"));
83 | ret.Add(new XboxLocale("ru-RU", "\u0420\u043e\u0441\u0441\u0438\u044f - \u0420\u0443\u0441\u0441\u043a\u0438\u0439"));
84 | ret.Add(new XboxLocale("en-AU", "Australia - English"));
85 | ret.Add(new XboxLocale("en-HK", "Hong Kong SAR / Macao SAR - English"));
86 | ret.Add(new XboxLocale("en-IN", "India - English"));
87 | ret.Add(new XboxLocale("id-ID", "Indonesia - Bahasa Indonesia"));
88 | ret.Add(new XboxLocale("en-MY", "Malaysia - English"));
89 | ret.Add(new XboxLocale("en-NZ", "New Zealand - English"));
90 | ret.Add(new XboxLocale("en-PH", "Philippines - English"));
91 | ret.Add(new XboxLocale("en-SG", "Singapore - English"));
92 | ret.Add(new XboxLocale("vi-VN", "Vi\u1ec7t Nam - Ti\xea\u0301ng vi\u1ec7t"));
93 | ret.Add(new XboxLocale("th-TH", "\u0e44\u0e17\u0e22 - \u0e44\u0e17\u0e22"));
94 | ret.Add(new XboxLocale("ko-KR", "\ub300\ud55c\ubbfc\uad6d - \ud55c\uad6d\uc5b4"));
95 | ret.Add(new XboxLocale("zh-CN", "\u4e2d\u56fd - \u4e2d\u6587"));
96 | ret.Add(new XboxLocale("zh-TW", "\u53f0\u7063 - \u7e41\u9ad4\u4e2d\u6587"));
97 | ret.Add(new XboxLocale("ja-JP", "\u65e5\u672c - \u65e5\u672c\u8a9e"));
98 | ret.Add(new XboxLocale("zh-HK", "\u9999\u6e2f\u7279\u5225\u884c\u653f\u5340/\u6fb3\u9580\u7279\u5225\u884c\u653f\u5340 - \u7e41\u9ad4\u4e2d\u6587"));
99 | ret.Add(new XboxLocale("en-ZA", "South Africa - English"));
100 | ret.Add(new XboxLocale("tr-TR", "T\xfcrkiye - T\xfcrk\xe7e"));
101 | ret.Add(new XboxLocale("he-IL", "\u05d9\u05e9\u05e8\u05d0\u05dc - \u05e2\u05d1\u05e8\u05d9\u05ea"));
102 | ret.Add(new XboxLocale("ar-AE", "\u0627\u0644\u0625\u0645\u0627\u0631\u0627\u062a \u0627\u0644\u0639\u0631\u0628\u064a\u0629 \u0627\u0644\u0645\u062a\u062d\u062f\u0629 - \u0627\u0644\u0639\u0631\u0628\u064a\u0629"));
103 | ret.Add(new XboxLocale("ar-SA", "\u0627\u0644\u0645\u0645\u0644\u0643\u0629 \u0627\u0644\u0639\u0631\u0628\u064a\u0629 \u0627\u0644\u0633\u0639\u0648\u062f\u064a\u0629 - \u0627\u0644\u0639\u0631\u0628\u064a\u0629"));
104 | ret.Sort((l1, l2) => string.CompareOrdinal(l1.ToString(), l2.ToString()));
105 | return ret.ToArray();
106 | }
107 | catch { return new XboxLocale[0]; }
108 | }
109 | }
110 |
111 | public class XboxTitleInfo {
112 | public enum XboxAssetType {
113 | Icon,
114 | Banner,
115 | Background,
116 | Screenshot
117 | }
118 |
119 | public string Title { get; private set; }
120 |
121 | public string TitleId { get; private set; }
122 |
123 | public string Locale { get; private set; }
124 |
125 | public XboxAssetInfo[] AssetsInfo { get; private set; }
126 |
127 | public XboxAsset[] Assets {
128 | get {
129 | if(AssetsInfo.Any(info => !info.HaveAsset))
130 | XboxAssetDownloader.SendStatusChanged(string.Format("Downloading assets for {0}...", Title));
131 | var ret = AssetsInfo.Select(info => info.GetAsset()).ToArray();
132 | return ret;
133 | }
134 | }
135 |
136 | public XboxAsset[] IconAssets {
137 | get {
138 | if(AssetsInfo.Where(info => info.AssetType == XboxAssetType.Icon).Any(info => !info.HaveAsset))
139 | XboxAssetDownloader.SendStatusChanged(string.Format("Downloading icon assets for {0}...", Title));
140 | var ret = AssetsInfo.Where(info => info.AssetType == XboxAssetType.Icon).Select(info => info.GetAsset()).ToArray();
141 | return ret;
142 | }
143 | }
144 |
145 | public XboxAsset[] BannerAssets {
146 | get {
147 | if(AssetsInfo.Where(info => info.AssetType == XboxAssetType.Banner).Any(info => !info.HaveAsset))
148 | XboxAssetDownloader.SendStatusChanged(string.Format("Downloading banner assets for {0}...", Title));
149 | var ret = AssetsInfo.Where(info => info.AssetType == XboxAssetType.Banner).Select(info => info.GetAsset()).ToArray();
150 | return ret;
151 | }
152 | }
153 |
154 | public XboxAsset[] BackgroundAssets {
155 | get {
156 | if(AssetsInfo.Where(info => info.AssetType == XboxAssetType.Background).Any(info => !info.HaveAsset))
157 | XboxAssetDownloader.SendStatusChanged(string.Format("Downloading background assets for {0}...", Title));
158 | var ret = AssetsInfo.Where(info => info.AssetType == XboxAssetType.Background).Select(info => info.GetAsset()).ToArray();
159 | return ret;
160 | }
161 | }
162 |
163 | public XboxAsset[] ScreenshotsAssets {
164 | get {
165 | if(AssetsInfo.Where(info => info.AssetType == XboxAssetType.Screenshot).Any(info => !info.HaveAsset))
166 | XboxAssetDownloader.SendStatusChanged(string.Format("Downloading screenshot assets for {0}...", Title));
167 | var ret = AssetsInfo.Where(info => info.AssetType == XboxAssetType.Screenshot).Select(info => info.GetAsset()).ToArray();
168 | return ret;
169 | }
170 | }
171 |
172 | private static void ParseXml(Stream xmlData, XboxTitleInfo titleInfo) {
173 | XboxAssetDownloader.SendStatusChanged("Parsing Title/Asset info...");
174 | var ret = new List();
175 | using(var xml = XmlReader.Create(xmlData)) {
176 | while(xml.Read()) {
177 | if(!xml.IsStartElement())
178 | continue;
179 | if (xml.Name.ToLower() == "live:fulltitle") {
180 | xml.Read();
181 | titleInfo.Title = xml.Value;
182 | continue;
183 | }
184 | // add every 'live:fileurl' within 'live:slideshows' as a screenshot asset
185 | if (xml.Name.ToLower() == "live:slideshows") {
186 | while (xml.Read()) {
187 | if (!xml.IsStartElement() && xml.Name.ToLower() == "live:slideshows") {
188 | break;
189 | }
190 | if (xml.IsStartElement() && xml.Name.ToLower() == "live:fileurl") {
191 | xml.Read();
192 | var url = new Uri(xml.Value);
193 | ret.Add(new XboxAssetInfo(url, XboxAssetType.Screenshot, titleInfo));
194 | }
195 | }
196 | }
197 | // add every 'live:image' within 'live:images' that has both 'live:relationshiptype' and 'live:fileurl' properties as an asset
198 | // the value of 'live:relationshiptype' is used to determine the asset type
199 | if (xml.Name.ToLower() == "live:images") {
200 | var imageRelationshipType = "";
201 | var imageFileUrl = "";
202 | while (xml.Read()) {
203 | if (!xml.IsStartElement() && xml.Name.ToLower() == "live:images") {
204 | break;
205 | }
206 | if (!xml.IsStartElement() && xml.Name.ToLower() == "live:image") {
207 | if (imageRelationshipType != "" && imageFileUrl != "") {
208 | switch (imageRelationshipType) {
209 | case "15":
210 | ret.Add(new XboxAssetInfo(new Uri(imageFileUrl), XboxAssetType.Icon, titleInfo));
211 | break;
212 | case "23":
213 | ret.Add(new XboxAssetInfo(new Uri(imageFileUrl), XboxAssetType.Icon, titleInfo));
214 | break;
215 | case "25":
216 | ret.Add(new XboxAssetInfo(new Uri(imageFileUrl), XboxAssetType.Background, titleInfo));
217 | break;
218 | case "27":
219 | ret.Add(new XboxAssetInfo(new Uri(imageFileUrl), XboxAssetType.Banner, titleInfo));
220 | break;
221 | default:
222 | break;
223 | }
224 | }
225 | imageRelationshipType = "";
226 | imageFileUrl = "";
227 | continue;
228 | }
229 | if (xml.IsStartElement() && xml.Name.ToLower() == "live:relationshiptype") {
230 | xml.Read();
231 | imageRelationshipType = xml.Value;
232 | }
233 | if (xml.IsStartElement() && xml.Name.ToLower() == "live:fileurl") {
234 | xml.Read();
235 | imageFileUrl = xml.Value;
236 | }
237 | }
238 | }
239 | }
240 | }
241 | titleInfo.AssetsInfo = ret.ToArray();
242 | XboxAssetDownloader.SendStatusChanged("Finished parsing Title/Asset info...");
243 | }
244 |
245 | public static XboxTitleInfo FromTitleId(uint titleId, XboxLocale locale) {
246 | var ret = new XboxTitleInfo {
247 | TitleId = string.Format("{0:X08}", titleId),
248 | Locale = locale.ToString()
249 | };
250 | var wc = new WebClient();
251 | var url =
252 | string.Format(
253 | "http://catalog-cdn.xboxlive.com/Catalog/Catalog.asmx/Query?methodName=FindGames&Names=Locale&Values={0}&Names=LegalLocale&Values={0}&Names=Store&Values=1&Names=PageSize&Values=100&Names=PageNum&Values=1&Names=DetailView&Values=5&Names=OfferFilterLevel&Values=1&Names=MediaIds&Values=66acd000-77fe-1000-9115-d802{1:X8}&Names=UserTypes&Values=2&Names=MediaTypes&Values=1&Names=MediaTypes&Values=21&Names=MediaTypes&Values=23&Names=MediaTypes&Values=37&Names=MediaTypes&Values=46",
254 | locale.Locale, titleId);
255 | XboxAssetDownloader.SendStatusChanged("Downloading title/asset information...");
256 | using(var stream = new MemoryStream(wc.DownloadData(url)))
257 | ParseXml(stream, ret);
258 | return ret;
259 | }
260 |
261 | public class XboxAsset {
262 | public readonly XboxAssetType AssetType;
263 |
264 | public readonly Image Image;
265 |
266 | public XboxAsset(Image image, XboxAssetType assetType) {
267 | Image = image;
268 | AssetType = assetType;
269 | }
270 | }
271 |
272 | public class XboxAssetInfo {
273 | public readonly XboxAssetType AssetType;
274 | public readonly Uri AssetUrl;
275 | private readonly XboxTitleInfo _titleInfo;
276 | private XboxAsset _asset;
277 |
278 | public XboxAssetInfo(Uri assetUrl, XboxAssetType assetType, XboxTitleInfo titleInfo) {
279 | AssetUrl = assetUrl;
280 | AssetType = assetType;
281 | _titleInfo = titleInfo;
282 | }
283 |
284 | public bool HaveAsset { get { return _asset != null; } }
285 |
286 | public XboxAsset GetAsset() {
287 | if(_asset != null)
288 | return _asset; // We already have it
289 | var wc = new WebClient();
290 | var data = wc.DownloadData(AssetUrl);
291 | var ms = new MemoryStream(data);
292 | var img = Image.FromStream(ms);
293 | return _asset = new XboxAsset(img, AssetType);
294 | }
295 |
296 | public override string ToString() { return string.Format("{0} [ {1} ] {2}", _titleInfo.Title, _titleInfo.TitleId, AssetType); }
297 | }
298 | }
299 |
300 | public class XboxLocale {
301 | public readonly string Locale;
302 |
303 | private readonly string _name;
304 |
305 | public XboxLocale(string locale, string name) {
306 | Locale = locale;
307 | _name = name;
308 | }
309 |
310 | public override string ToString() { return string.Format("{0} [ {1} ]", _name, Locale); }
311 | }
312 |
313 | [DataContract] public class XboxKeywordResponse {
314 | [DataMember(Name = "entries")] public Entry[] Entries { get; set; }
315 |
316 | [DataContract] public class Entry {
317 | [DataMember(Name = "detailsUrl")] public string DetailsUrl { get; set; }
318 | //There is more data sent both here and ^, but we only need this, so i only added that...
319 | }
320 | }
321 | }
322 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Classes/XboxUnity.cs:
--------------------------------------------------------------------------------
1 | //
2 | // XboxUnity.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Classes {
9 | using System;
10 | using System.Drawing;
11 | using System.IO;
12 | using System.Linq;
13 | using System.Net;
14 | using System.Runtime.Serialization;
15 | using System.Runtime.Serialization.Json;
16 |
17 | internal static class XboxUnity {
18 | private static readonly DataContractJsonSerializer Serializer = new DataContractJsonSerializer(typeof(UnityResponse[]));
19 |
20 | private static string GetUnityUrl(string searchTerm) { return string.Format("http://xboxunity.net/api/Covers/{0}", WebUtility.UrlEncode(searchTerm)); }
21 |
22 | public static XboxUnityAsset[] GetUnityCoverInfo(string searchTerm) {
23 | using(var wc = new WebClient()) {
24 | try {
25 | var stream = wc.OpenRead(GetUnityUrl(searchTerm));
26 | return stream != null ? ((UnityResponse[])Serializer.ReadObject(stream)).Select(t => new XboxUnityAsset(t)).ToArray() : new XboxUnityAsset[0];
27 | }
28 | catch(Exception ex) {
29 | MainWindow.SaveError(ex);
30 | return new XboxUnityAsset[0];
31 | }
32 | }
33 | }
34 |
35 | public static string GetHomebrewTitleFromFtp(string path) {
36 | var data = App.FtpOperations.GetAssetData("GameCoverInfo.bin", path);
37 | if(data == null || data.Length < 10)
38 | return "N/A";
39 | using(var ms = new MemoryStream(data)) {
40 | var response = (UnityResponse[])Serializer.ReadObject(ms);
41 | return response.Length <= 0 ? "N/A" : response[0].Name;
42 | }
43 | }
44 |
45 | [DataContract] internal class UnityResponse {
46 | [DataMember(Name = "titleid")] public string TitleId { get; set; }
47 |
48 | [DataMember(Name = "name")] public string Name { get; set; }
49 |
50 | [DataMember(Name = "official")] public bool Official { get; set; }
51 |
52 | [DataMember(Name = "filesize")] public string FileSize { get; set; }
53 |
54 | [DataMember(Name = "url")] public string Url { get; set; }
55 |
56 | [DataMember(Name = "front")] public string Front { get; set; }
57 |
58 | [DataMember(Name = "thumbnail")] public string Thumbnail { get; set; }
59 |
60 | [DataMember(Name = "author")] public string Author { get; set; }
61 |
62 | [DataMember(Name = "uploaddate")] public string UploadDate { get; set; }
63 |
64 | [DataMember(Name = "rating")] public string Rating { get; set; }
65 |
66 | [DataMember(Name = "link")] public string Link { get; set; }
67 | }
68 |
69 | public class XboxUnityAsset {
70 | private readonly UnityResponse _unityResponse;
71 | private Image _cover;
72 |
73 | public XboxUnityAsset(UnityResponse response) { _unityResponse = response; }
74 |
75 | public bool HaveAsset { get { return _cover != null; } }
76 |
77 | public string Title { get { return _unityResponse.Name; } }
78 |
79 | private static Image GetImage(string url) {
80 | var wc = new WebClient();
81 | var data = wc.DownloadData(url);
82 | var ms = new MemoryStream(data);
83 | return Image.FromStream(ms);
84 | }
85 |
86 | public Image GetCover() {
87 | if(_cover != null)
88 | return _cover;
89 | return _cover = GetImage(_unityResponse.Url);
90 | }
91 |
92 | public override string ToString() {
93 | return string.Format(_unityResponse.Official ? "Official cover for {0} Rating: {1}" : "Cover for {0} Rating: {1}", _unityResponse.Name, _unityResponse.Rating ?? "N/A");
94 | }
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/BackgroundControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
13 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/BackgroundControl.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // BackgroundControl.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.ComponentModel;
11 | using System.Drawing.Imaging;
12 | using System.IO;
13 | using System.Windows;
14 | using System.Windows.Controls;
15 | using System.Windows.Media.Imaging;
16 | using Classes;
17 | using Microsoft.Win32;
18 | using Image = System.Drawing.Image;
19 | using Size = System.Drawing.Size;
20 |
21 | ///
22 | /// Interaction logic for BackgroundControl.xaml
23 | ///
24 | public partial class BackgroundControl {
25 | private readonly MainWindow _main;
26 | internal bool HavePreview;
27 | private AuroraAsset.AssetFile _assetFile;
28 | private MemoryStream _memoryStream;
29 |
30 | public BackgroundControl(MainWindow main) {
31 | InitializeComponent();
32 | _main = main;
33 | _assetFile = new AuroraAsset.AssetFile();
34 | }
35 |
36 | public void Save() {
37 | var sfd = new SaveFileDialog();
38 | if(sfd.ShowDialog() == true)
39 | Save(sfd.FileName);
40 | }
41 |
42 | public void Save(string filename) { File.WriteAllBytes(filename, _assetFile.FileData); }
43 |
44 | public void Reset() {
45 | SetPreview(null);
46 | _assetFile = new AuroraAsset.AssetFile();
47 | }
48 |
49 | public void Load(AuroraAsset.AssetFile asset) {
50 | _assetFile.SetBackground(asset);
51 | Dispatcher.Invoke(new Action(() => SetPreview(_assetFile.GetBackground())));
52 | }
53 |
54 | private void SetPreview(Image img) {
55 | if(img == null) {
56 | PreviewImg.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Placeholders/background.png", UriKind.Absolute));
57 | HavePreview = false;
58 | return;
59 | }
60 | if(_memoryStream != null)
61 | _memoryStream.Close();
62 | var bi = new BitmapImage();
63 | _memoryStream = new MemoryStream();
64 | img.Save(_memoryStream, ImageFormat.Bmp);
65 | _memoryStream.Seek(0, SeekOrigin.Begin);
66 | bi.BeginInit();
67 | bi.StreamSource = _memoryStream;
68 | bi.EndInit();
69 | PreviewImg.Source = bi;
70 | HavePreview = true;
71 | }
72 |
73 | public void Load(Image img) {
74 | var shouldUseCompression = false;
75 | Dispatcher.Invoke(new Action(() => shouldUseCompression = _main.UseCompression.IsChecked));
76 | _assetFile.SetBackground(img, shouldUseCompression);
77 | Dispatcher.Invoke(new Action(() => SetPreview(img)));
78 | }
79 |
80 | private void OnDragEnter(object sender, DragEventArgs e) { _main.OnDragEnter(sender, e); }
81 |
82 | private void OnDrop(object sender, DragEventArgs e) { _main.DragDrop(this, e); }
83 |
84 | internal void SaveImageToFileOnClick(object sender, RoutedEventArgs e) { MainWindow.SaveToFile(_assetFile.GetBackground(), "Select where to save the Background", "background.png"); }
85 |
86 | internal void SelectNewBackground(object sender, RoutedEventArgs e) {
87 | var bw = new BackgroundWorker();
88 | bw.DoWork += (o, args) => {
89 | var img = _main.LoadImage("Select new background", "background.png", new Size(1280, 720));
90 | if(img != null)
91 | Load(img);
92 | };
93 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
94 | bw.RunWorkerAsync();
95 | }
96 |
97 | private void OnContextMenuOpening(object sender, ContextMenuEventArgs e) { SaveContextMenuItem.IsEnabled = HavePreview; }
98 |
99 | public byte[] GetData() { return _assetFile.FileData; }
100 | }
101 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/BoxartControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
13 |
16 |
17 |
18 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/BoxartControl.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // BoxartControl.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.ComponentModel;
11 | using System.Drawing.Imaging;
12 | using System.IO;
13 | using System.Windows;
14 | using System.Windows.Controls;
15 | using System.Windows.Media.Imaging;
16 | using Classes;
17 | using Microsoft.Win32;
18 | using Image = System.Drawing.Image;
19 | using Size = System.Drawing.Size;
20 |
21 | ///
22 | /// Interaction logic for BoxartControl.xaml
23 | ///
24 | public partial class BoxartControl {
25 | private readonly MainWindow _main;
26 | internal bool HavePreview;
27 | private AuroraAsset.AssetFile _assetFile;
28 | private MemoryStream _memoryStream;
29 |
30 | public BoxartControl(MainWindow main) {
31 | InitializeComponent();
32 | _main = main;
33 | _assetFile = new AuroraAsset.AssetFile();
34 | }
35 |
36 | public void Save() {
37 | var sfd = new SaveFileDialog();
38 | if(sfd.ShowDialog() == true)
39 | Save(sfd.FileName);
40 | }
41 |
42 | public void Save(string filename) { File.WriteAllBytes(filename, _assetFile.FileData); }
43 |
44 | public void Reset() {
45 | SetPreview(null);
46 | _assetFile = new AuroraAsset.AssetFile();
47 | }
48 |
49 | public void Load(AuroraAsset.AssetFile asset) {
50 | _assetFile.SetBoxart(asset);
51 | Dispatcher.Invoke(new Action(() => SetPreview(_assetFile.GetBoxart())));
52 | }
53 |
54 | private void SetPreview(Image img) {
55 | if(img == null) {
56 | PreviewImg.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Placeholders/cover.png", UriKind.Absolute));
57 | HavePreview = false;
58 | return;
59 | }
60 | if(_memoryStream != null)
61 | _memoryStream.Close();
62 | var bi = new BitmapImage();
63 | _memoryStream = new MemoryStream();
64 | img.Save(_memoryStream, ImageFormat.Bmp);
65 | _memoryStream.Seek(0, SeekOrigin.Begin);
66 | bi.BeginInit();
67 | bi.StreamSource = _memoryStream;
68 | bi.EndInit();
69 | PreviewImg.Source = bi;
70 | HavePreview = true;
71 | }
72 |
73 | public void Load(Image img) {
74 | var shouldUseCompression = false;
75 | Dispatcher.Invoke(new Action(() => shouldUseCompression = _main.UseCompression.IsChecked));
76 | _assetFile.SetBoxart(img, shouldUseCompression);
77 | Dispatcher.Invoke(new Action(() => SetPreview(img)));
78 | }
79 |
80 | private void OnDragEnter(object sender, DragEventArgs e) { _main.OnDragEnter(sender, e); }
81 |
82 | private void OnDrop(object sender, DragEventArgs e) { _main.DragDrop(this, e); }
83 |
84 | internal void SaveImageToFileOnClick(object sender, RoutedEventArgs e) { MainWindow.SaveToFile(_assetFile.GetBoxart(), "Select where to save the Cover", "cover.png"); }
85 |
86 | internal void SelectNewCover(object sender, RoutedEventArgs e) {
87 | var bw = new BackgroundWorker();
88 | bw.DoWork += (o, args) => {
89 | var img = _main.LoadImage("Select new cover", "cover.png", new Size(900, 600));
90 | if(img != null)
91 | Load(img);
92 | };
93 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
94 | bw.RunWorkerAsync();
95 | }
96 |
97 | private void OnContextMenuOpening(object sender, ContextMenuEventArgs e) { SaveContextMenuItem.IsEnabled = HavePreview; }
98 |
99 | public byte[] GetData() { return _assetFile.FileData; }
100 | }
101 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/CircularProgressBar.xaml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
56 |
57 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/CircularProgressBar.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // CircularProgressBar.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.Windows;
11 | using System.Windows.Controls;
12 | using System.Windows.Input;
13 | using System.Windows.Threading;
14 |
15 | public partial class CircularProgressBar {
16 | #region Data
17 |
18 | private readonly DispatcherTimer _animationTimer;
19 |
20 | #endregion
21 |
22 | #region Constructor
23 |
24 | public CircularProgressBar() {
25 | InitializeComponent();
26 |
27 | _animationTimer = new DispatcherTimer(DispatcherPriority.ContextIdle) {
28 | Interval = new TimeSpan(0, 0, 0, 0, 75)
29 | };
30 | }
31 |
32 | #endregion
33 |
34 | #region Private Methods
35 |
36 | private void Start() {
37 | Mouse.OverrideCursor = Cursors.Wait;
38 | _animationTimer.Tick += HandleAnimationTick;
39 | _animationTimer.Start();
40 | }
41 |
42 | private void Stop() {
43 | _animationTimer.Stop();
44 | Mouse.OverrideCursor = Cursors.Arrow;
45 | _animationTimer.Tick -= HandleAnimationTick;
46 | }
47 |
48 | private void HandleAnimationTick(object sender, EventArgs e) { SpinnerRotate.Angle = (SpinnerRotate.Angle + 36) % 360; }
49 |
50 | private void HandleLoaded(object sender, RoutedEventArgs e) {
51 | const double offset = Math.PI;
52 | const double step = Math.PI * 2 / 10.0;
53 |
54 | SetPosition(C0, offset, 0.0, step);
55 | SetPosition(C1, offset, 1.0, step);
56 | SetPosition(C2, offset, 2.0, step);
57 | SetPosition(C3, offset, 3.0, step);
58 | SetPosition(C4, offset, 4.0, step);
59 | SetPosition(C5, offset, 5.0, step);
60 | SetPosition(C6, offset, 6.0, step);
61 | SetPosition(C7, offset, 7.0, step);
62 | SetPosition(C8, offset, 8.0, step);
63 | }
64 |
65 | private static void SetPosition(DependencyObject ellipse, double offset, double posOffSet, double step) {
66 | ellipse.SetValue(Canvas.LeftProperty, 50.0 + Math.Sin(offset + posOffSet * step) * 50.0);
67 | ellipse.SetValue(Canvas.TopProperty, 50 + Math.Cos(offset + posOffSet * step) * 50.0);
68 | }
69 |
70 | private void HandleUnloaded(object sender, RoutedEventArgs e) { Stop(); }
71 |
72 | private void HandleVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) {
73 | var isVisible = (bool)e.NewValue;
74 |
75 | if(isVisible)
76 | Start();
77 | else
78 | Stop();
79 | }
80 |
81 | #endregion
82 | }
83 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/FtpAssetsControl.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
35 |
37 |
39 |
41 |
43 |
45 |
47 |
48 |
49 |
50 |
51 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
127 |
128 |
129 |
130 |
131 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/FtpAssetsControl.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // FtpAssetsControl.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 13/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.ComponentModel;
11 | using System.IO;
12 | using System.Linq;
13 | using System.Net.NetworkInformation;
14 | using System.Net.Sockets;
15 | using System.Threading;
16 | using System.Windows;
17 | using System.Windows.Controls;
18 | using System.Windows.Data;
19 | using System.Windows.Threading;
20 | using AuroraAssetEditor.Models;
21 | using Classes;
22 | using Helpers;
23 |
24 | ///
25 | /// Interaction logic for FtpAssetsControl.xaml
26 | ///
27 | public partial class FtpAssetsControl {
28 | private readonly ThreadSafeObservableCollection _assetsList = new ThreadSafeObservableCollection();
29 | private readonly CollectionViewSource _assetsViewSource = new CollectionViewSource();
30 | private readonly ICollectionView _assetView;
31 | private readonly BackgroundControl _background;
32 | private readonly BoxartControl _boxart;
33 | private readonly IconBannerControl _iconBanner;
34 | private readonly MainWindow _main;
35 | private readonly ScreenshotsControl _screenshots;
36 | private byte[] _buffer;
37 | private bool _isBusy, _isError;
38 |
39 | public FtpAssetsControl(MainWindow main, BoxartControl boxart, BackgroundControl background, IconBannerControl iconBanner, ScreenshotsControl screenshots) {
40 | InitializeComponent();
41 | _assetsViewSource.Source = _assetsList;
42 | _main = main;
43 | _boxart = boxart;
44 | _background = background;
45 | _iconBanner = iconBanner;
46 | _screenshots = screenshots;
47 | App.FtpOperations.StatusChanged += (sender, args) => Dispatcher.Invoke(new Action(() => Status.Text = args.StatusMessage));
48 | FtpAssetsBox.ItemsSource = _assetView = _assetsViewSource.View;
49 | if(!App.FtpOperations.HaveSettings) {
50 | var ip = GetActiveIp();
51 | var index = ip.LastIndexOf('.');
52 | if(ip.Length > 0 && index > 0)
53 | IpBox.Text = ip.Substring(0, index + 1);
54 | }
55 | else {
56 | IpBox.Text = App.FtpOperations.IpAddress;
57 | UserBox.Text = App.FtpOperations.Username;
58 | PassBox.Text = App.FtpOperations.Password;
59 | PortBox.Text = App.FtpOperations.Port;
60 | }
61 | }
62 |
63 | private static string GetActiveIp() {
64 | foreach(var unicastAddress in
65 | NetworkInterface.GetAllNetworkInterfaces().Where(f => f.OperationalStatus == OperationalStatus.Up).Select(f => f.GetIPProperties()).Where(
66 | ipInterface =>
67 | ipInterface.GatewayAddresses.Count > 0)
68 | .SelectMany(
69 | ipInterface =>
70 | ipInterface.UnicastAddresses.Where(
71 | unicastAddress =>
72 | (unicastAddress.Address.AddressFamily == AddressFamily.InterNetwork) && (unicastAddress.IPv4Mask.ToString() != "0.0.0.0")))
73 | )
74 | return unicastAddress.Address.ToString();
75 | return "";
76 | }
77 |
78 | private void TestConnectionClick(object sender, RoutedEventArgs e) {
79 | var ip = IpBox.Text;
80 | var user = UserBox.Text;
81 | var pass = PassBox.Text;
82 | var port = PortBox.Text;
83 | var bw = new BackgroundWorker();
84 | bw.DoWork += (o, args) => App.FtpOperations.TestConnection(ip, user, pass, port);
85 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
86 | _main.BusyIndicator.Visibility = Visibility.Visible;
87 | Status.Text = string.Format("Running a connection test to {0}", IpBox.Text);
88 | bw.RunWorkerAsync();
89 | }
90 |
91 | private void SaveSettingsClick(object sender, RoutedEventArgs e) { App.FtpOperations.SaveSettings(IpBox.Text, UserBox.Text, PassBox.Text, PortBox.Text); }
92 |
93 | private void FtpAssetsBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
94 | {
95 | if (FtpAssetsBox.SelectedItem is Classes.AuroraDbManager.ContentItem selectedAsset)
96 | {
97 | var newGame = new Game
98 | {
99 | Title = selectedAsset.TitleName,
100 | TitleId = selectedAsset.TitleId,
101 | DbId = selectedAsset.DatabaseId,
102 | IsGameSelected = true
103 | };
104 |
105 | GlobalState.CurrentGame = newGame;
106 | }
107 | }
108 |
109 | private void GetAssetsClick(object sender, RoutedEventArgs e) {
110 | _assetsList.Clear();
111 | if(!App.FtpOperations.HaveSettings)
112 | return;
113 | var bw = new BackgroundWorker();
114 | bw.DoWork += (o, args) => {
115 | try {
116 | var path = Path.Combine(Path.GetTempPath(), "AuroraAssetEditor.db");
117 | if(!App.FtpOperations.DownloadContentDb(path))
118 | return;
119 | foreach(var title in AuroraDbManager.GetDbTitles(path))
120 | _assetsList.Add(title);
121 | args.Result = true;
122 | }
123 | catch(Exception ex) {
124 | MainWindow.SaveError(ex);
125 | args.Result = false;
126 | }
127 | };
128 | bw.RunWorkerCompleted += (o, args) => {
129 | _main.BusyIndicator.Visibility = Visibility.Collapsed;
130 | if((bool)args.Result)
131 | Status.Text = "Finished grabbing FTP Assets information successfully...";
132 | else
133 | Status.Text = "There was an error, check error.log for more information...";
134 | };
135 | _main.BusyIndicator.Visibility = Visibility.Visible;
136 | Status.Text = "Grabbing FTP Assets information...";
137 | bw.RunWorkerAsync();
138 | }
139 |
140 | private void ProcessAsset(Task task, bool shouldHideWhenDone = true) {
141 | _isError = false;
142 | AuroraDbManager.ContentItem asset = null;
143 | Dispatcher.InvokeIfRequired(() => asset = FtpAssetsBox.SelectedItem as AuroraDbManager.ContentItem, DispatcherPriority.Normal);
144 | if(asset == null)
145 | return;
146 | var bw = new BackgroundWorker();
147 | bw.DoWork += (sender, args) => {
148 | try {
149 | switch(task) {
150 | case Task.GetBoxart:
151 | _buffer = asset.GetBoxart();
152 | break;
153 | case Task.GetBackground:
154 | _buffer = asset.GetBackground();
155 | break;
156 | case Task.GetIconBanner:
157 | _buffer = asset.GetIconBanner();
158 | break;
159 | case Task.GetScreenshots:
160 | _buffer = asset.GetScreenshots();
161 | break;
162 | case Task.SetBoxart:
163 | asset.SaveAsBoxart(_buffer);
164 | break;
165 | case Task.SetBackground:
166 | asset.SaveAsBackground(_buffer);
167 | break;
168 | case Task.SetIconBanner:
169 | asset.SaveAsIconBanner(_buffer);
170 | break;
171 | case Task.SetScreenshots:
172 | asset.SaveAsScreenshots(_buffer);
173 | break;
174 | }
175 | args.Result = true;
176 | }
177 | catch(Exception ex) {
178 | MainWindow.SaveError(ex);
179 | args.Result = false;
180 | }
181 | };
182 | bw.RunWorkerCompleted += (sender, args) => {
183 | if(shouldHideWhenDone)
184 | Dispatcher.InvokeIfRequired(() => _main.BusyIndicator.Visibility = Visibility.Collapsed, DispatcherPriority.Normal);
185 | var isGet = true;
186 | if((bool)args.Result) {
187 | if(_buffer.Length > 0) {
188 | var aurora = new AuroraAsset.AssetFile(_buffer);
189 | switch(task) {
190 | case Task.GetBoxart:
191 | _boxart.Load(aurora);
192 | Dispatcher.InvokeIfRequired(() => _main.BoxartTab.IsSelected = true, DispatcherPriority.Normal);
193 | break;
194 | case Task.GetBackground:
195 | _background.Load(aurora);
196 | Dispatcher.InvokeIfRequired(() => _main.BackgroundTab.IsSelected = true, DispatcherPriority.Normal);
197 | break;
198 | case Task.GetIconBanner:
199 | _iconBanner.Load(aurora);
200 | Dispatcher.InvokeIfRequired(() => _main.IconBannerTab.IsSelected = true, DispatcherPriority.Normal);
201 | break;
202 | case Task.GetScreenshots:
203 | _screenshots.Load(aurora);
204 | Dispatcher.InvokeIfRequired(() => _main.ScreenshotsTab.IsSelected = true, DispatcherPriority.Normal);
205 | break;
206 | default:
207 | isGet = false;
208 | break;
209 | }
210 | }
211 | if(shouldHideWhenDone && isGet)
212 | Dispatcher.InvokeIfRequired(() => Status.Text = "Finished grabbing assets from FTP", DispatcherPriority.Normal);
213 | else if(shouldHideWhenDone)
214 | Dispatcher.InvokeIfRequired(() => Status.Text = "Finished saving assets to FTP", DispatcherPriority.Normal);
215 | }
216 | else {
217 | switch(task) {
218 | case Task.GetBoxart:
219 | case Task.GetBackground:
220 | case Task.GetIconBanner:
221 | case Task.GetScreenshots:
222 | break;
223 | default:
224 | isGet = false;
225 | break;
226 | }
227 | if(isGet)
228 | Dispatcher.InvokeIfRequired(() => Status.Text = "Failed getting asset data... See error.log for more information...", DispatcherPriority.Normal);
229 | else
230 | Dispatcher.InvokeIfRequired(() => Status.Text = "Failed saving asset data... See error.log for more information...", DispatcherPriority.Normal);
231 | _isError = true;
232 | }
233 | _isBusy = false;
234 | };
235 | Dispatcher.InvokeIfRequired(() => _main.BusyIndicator.Visibility = Visibility.Visible, DispatcherPriority.Normal);
236 | _isBusy = true;
237 | bw.RunWorkerAsync();
238 | }
239 |
240 | private void GetBoxartClick(object sender, RoutedEventArgs e) { ProcessAsset(Task.GetBoxart); }
241 |
242 | private void GetBackgroundClick(object sender, RoutedEventArgs e) { ProcessAsset(Task.GetBackground); }
243 |
244 | private void GetIconBannerClick(object sender, RoutedEventArgs e) { ProcessAsset(Task.GetIconBanner); }
245 |
246 | private void GetScreenshotsClick(object sender, RoutedEventArgs e) { ProcessAsset(Task.GetScreenshots); }
247 |
248 | private void GetFtpAssetsClick(object sender, RoutedEventArgs e) {
249 | var bw = new BackgroundWorker();
250 | bw.DoWork += (o, args) => {
251 | ProcessAsset(Task.GetBoxart, false);
252 | while(_isBusy)
253 | Thread.Sleep(100);
254 | if(_isError)
255 | return;
256 | ProcessAsset(Task.GetBackground, false);
257 | while(_isBusy)
258 | Thread.Sleep(100);
259 | if(_isError)
260 | return;
261 | ProcessAsset(Task.GetIconBanner, false);
262 | while(_isBusy)
263 | Thread.Sleep(100);
264 | if(_isError)
265 | return;
266 | ProcessAsset(Task.GetScreenshots);
267 | };
268 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
269 | bw.RunWorkerAsync();
270 | }
271 |
272 | private void SaveFtpAssetsClick(object sender, RoutedEventArgs e) {
273 | var bw = new BackgroundWorker();
274 | bw.DoWork += (o, args) => {
275 | Dispatcher.InvokeIfRequired(() => _buffer = _boxart.GetData(), DispatcherPriority.Normal);
276 | ProcessAsset(Task.SetBoxart, false);
277 | while(_isBusy)
278 | Thread.Sleep(100);
279 | if(_isError)
280 | return;
281 | Dispatcher.InvokeIfRequired(() => _buffer = _background.GetData(), DispatcherPriority.Normal);
282 | ProcessAsset(Task.SetBackground, false);
283 | while(_isBusy)
284 | Thread.Sleep(100);
285 | if(_isError)
286 | return;
287 | Dispatcher.InvokeIfRequired(() => _buffer = _iconBanner.GetData(), DispatcherPriority.Normal);
288 | ProcessAsset(Task.SetIconBanner, false);
289 | while(_isBusy)
290 | Thread.Sleep(100);
291 | if(_isError)
292 | return;
293 | Dispatcher.InvokeIfRequired(() => _buffer = _screenshots.GetData(), DispatcherPriority.Normal);
294 | ProcessAsset(Task.SetScreenshots);
295 | };
296 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
297 | _main.BusyIndicator.Visibility = Visibility.Visible;
298 | bw.RunWorkerAsync();
299 | }
300 |
301 | private void SaveBoxartClick(object sender, RoutedEventArgs e) {
302 | var bw = new BackgroundWorker();
303 | bw.DoWork += (o, args) => {
304 | Dispatcher.InvokeIfRequired(() => _buffer = _boxart.GetData(), DispatcherPriority.Normal);
305 | ProcessAsset(Task.SetBoxart);
306 | };
307 | _main.BusyIndicator.Visibility = Visibility.Visible;
308 | bw.RunWorkerAsync();
309 | }
310 |
311 | private void SaveBackgroundClick(object sender, RoutedEventArgs e) {
312 | var bw = new BackgroundWorker();
313 | bw.DoWork += (o, args) => {
314 | Dispatcher.InvokeIfRequired(() => _buffer = _background.GetData(), DispatcherPriority.Normal);
315 | ProcessAsset(Task.SetBackground);
316 | };
317 | _main.BusyIndicator.Visibility = Visibility.Visible;
318 | bw.RunWorkerAsync();
319 | }
320 |
321 | private void SaveIconBannerClick(object sender, RoutedEventArgs e) {
322 | var bw = new BackgroundWorker();
323 | bw.DoWork += (o, args) => {
324 | Dispatcher.InvokeIfRequired(() => _buffer = _iconBanner.GetData(), DispatcherPriority.Normal);
325 | ProcessAsset(Task.SetIconBanner);
326 | };
327 | _main.BusyIndicator.Visibility = Visibility.Visible;
328 | bw.RunWorkerAsync();
329 | }
330 |
331 | private void SaveScreenshotsClick(object sender, RoutedEventArgs e) {
332 | var bw = new BackgroundWorker();
333 | bw.DoWork += (o, args) => {
334 | Dispatcher.InvokeIfRequired(() => _buffer = _screenshots.GetData(), DispatcherPriority.Normal);
335 | ProcessAsset(Task.SetScreenshots);
336 | };
337 | _main.BusyIndicator.Visibility = Visibility.Visible;
338 | bw.RunWorkerAsync();
339 | }
340 |
341 | private void OnDragEnter(object sender, DragEventArgs e) { _main.OnDragEnter(sender, e); }
342 |
343 | private void OnDrop(object sender, DragEventArgs e) { _main.DragDrop(this, e); }
344 |
345 | private void FtpAssetsBoxContextOpening(object sender, ContextMenuEventArgs e) {
346 | if(FtpAssetsBox.SelectedItem == null)
347 | e.Handled = true;
348 | }
349 |
350 | private void RemoveFtpAssetsClick(object sender, RoutedEventArgs e) {
351 | _boxart.Reset();
352 | _iconBanner.Reset();
353 | _background.Reset();
354 | _screenshots.Reset();
355 | SaveFtpAssetsClick(sender, e);
356 | }
357 |
358 | private void RemoveBoxartClick(object sender, RoutedEventArgs e) {
359 | _boxart.Reset();
360 | SaveBoxartClick(sender, e);
361 | }
362 |
363 | private void RemoveBackgroundClick(object sender, RoutedEventArgs e) {
364 | _background.Reset();
365 | SaveBackgroundClick(sender, e);
366 | }
367 |
368 | private void RemoveIconBannerClick(object sender, RoutedEventArgs e) {
369 | _iconBanner.Reset();
370 | SaveIconBannerClick(sender, e);
371 | }
372 |
373 | private void RemoveScreenshotsClick(object sender, RoutedEventArgs e) {
374 | _screenshots.Reset();
375 | SaveScreenshotsClick(sender, e);
376 | }
377 |
378 | private void TitleFilterChanged(Object sender, TextChangedEventArgs e) => FiltersChanged(TitleFilterBox.Text, TitleIdFilterBox.Text);
379 |
380 | private void TitleIdFilterChanged(Object sender, TextChangedEventArgs e) => FiltersChanged(TitleFilterBox.Text, TitleIdFilterBox.Text);
381 |
382 | private void FiltersChanged(string titleFilter, string titleIdFilter)
383 | {
384 | _assetView.Filter = item =>
385 | {
386 | var contentItem = item as AuroraDbManager.ContentItem;
387 | if (contentItem == null)
388 | return false;
389 | if (string.IsNullOrWhiteSpace(titleFilter) && string.IsNullOrWhiteSpace(titleIdFilter))
390 | return true;
391 | if (!string.IsNullOrWhiteSpace(titleFilter) && !string.IsNullOrWhiteSpace(titleIdFilter))
392 | return contentItem.TitleName.ToLower().Contains(titleFilter.ToLower()) && contentItem.TitleId.ToLower().Contains(titleIdFilter.ToLower());
393 | if (!string.IsNullOrWhiteSpace(titleFilter))
394 | return contentItem.TitleName.ToLower().Contains(titleFilter.ToLower());
395 | return contentItem.TitleId.ToLower().Contains(titleIdFilter.ToLower());
396 | };
397 | }
398 |
399 | private enum Task {
400 | GetBoxart,
401 | GetBackground,
402 | GetIconBanner,
403 | GetScreenshots,
404 | SetBoxart,
405 | SetBackground,
406 | SetIconBanner,
407 | SetScreenshots,
408 | }
409 | }
410 | }
411 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/IconBannerControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
16 |
18 |
21 |
22 |
23 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
33 |
34 |
37 |
38 |
39 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/IconBannerControl.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // IconBannerControl.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.ComponentModel;
11 | using System.Drawing.Imaging;
12 | using System.IO;
13 | using System.Windows;
14 | using System.Windows.Controls;
15 | using System.Windows.Media.Imaging;
16 | using Classes;
17 | using Microsoft.Win32;
18 | using Image = System.Drawing.Image;
19 | using Size = System.Drawing.Size;
20 |
21 | ///
22 | /// Interaction logic for IconBannerControl.xaml
23 | ///
24 | public partial class IconBannerControl {
25 | private readonly MainWindow _main;
26 | internal bool HaveBanner;
27 | internal bool HaveIcon;
28 | private AuroraAsset.AssetFile _assetFile;
29 |
30 | public IconBannerControl(MainWindow main) {
31 | InitializeComponent();
32 | _main = main;
33 | _assetFile = new AuroraAsset.AssetFile();
34 | }
35 |
36 | public void Save() {
37 | var sfd = new SaveFileDialog();
38 | if(sfd.ShowDialog() == true)
39 | Save(sfd.FileName);
40 | }
41 |
42 | public void Save(string filename) { File.WriteAllBytes(filename, _assetFile.FileData); }
43 |
44 | public void Reset() {
45 | SetPreview(null, true);
46 | SetPreview(null, false);
47 | _assetFile = new AuroraAsset.AssetFile();
48 | }
49 |
50 | public void Load(AuroraAsset.AssetFile asset) {
51 | _assetFile.SetIcon(asset);
52 | _assetFile.SetBanner(asset);
53 | Dispatcher.Invoke(new Action(() => SetPreview(_assetFile.GetIcon(), true)));
54 | Dispatcher.Invoke(new Action(() => SetPreview(_assetFile.GetBanner(), false)));
55 | }
56 |
57 | private void SetPreview(Image img, bool icon) {
58 | if(img == null) {
59 | if(icon) {
60 | PreviewIcon.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Placeholders/icon.png", UriKind.Absolute));
61 | HaveIcon = false;
62 | }
63 | else {
64 | PreviewBanner.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Placeholders/banner.png", UriKind.Absolute));
65 | HaveBanner = false;
66 | }
67 | return;
68 | }
69 | var bi = new BitmapImage();
70 | bi.BeginInit();
71 | var ms = new MemoryStream();
72 | img.Save(ms, ImageFormat.Bmp);
73 | ms.Seek(0, SeekOrigin.Begin);
74 | bi.StreamSource = ms;
75 | bi.EndInit();
76 | if(icon) {
77 | PreviewIcon.Source = bi;
78 | HaveIcon = true;
79 | }
80 | else {
81 | PreviewBanner.Source = bi;
82 | HaveBanner = true;
83 | }
84 | }
85 |
86 | public void Load(Image img, bool icon) {
87 | var shouldUseCompression = false;
88 | Dispatcher.Invoke(new Action(() => shouldUseCompression = _main.UseCompression.IsChecked));
89 | if(icon)
90 | _assetFile.SetIcon(img, shouldUseCompression);
91 | else
92 | _assetFile.SetBanner(img, shouldUseCompression);
93 | Dispatcher.Invoke(new Action(() => SetPreview(img, icon)));
94 | }
95 |
96 | private void OnDragEnter(object sender, DragEventArgs e) { _main.OnDragEnter(sender, e); }
97 |
98 | private void OnDrop(object sender, DragEventArgs e) { _main.DragDrop(this, e); }
99 |
100 | internal void SaveIconToFileOnClick(object sender, RoutedEventArgs e) { MainWindow.SaveToFile(_assetFile.GetIcon(), "Select where to save the Icon", "icon.png"); }
101 |
102 | internal void SaveBannerToFileOnClick(object sender, RoutedEventArgs e) { MainWindow.SaveToFile(_assetFile.GetBanner(), "Select where to save the Banner", "banner.png"); }
103 |
104 | internal void SelectNewIcon(object sender, RoutedEventArgs e) {
105 | var bw = new BackgroundWorker();
106 | bw.DoWork += (o, args) => {
107 | var img = _main.LoadImage("Select new icon", "icon.png", new Size(64, 64));
108 | if(img != null)
109 | Load(img, true);
110 | };
111 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
112 | bw.RunWorkerAsync();
113 | }
114 |
115 | internal void SelectNewBanner(object sender, RoutedEventArgs e) {
116 | var bw = new BackgroundWorker();
117 | bw.DoWork += (o, args) => {
118 | var img = _main.LoadImage("Select new banner", "banner.png", new Size(420, 96));
119 | if(img != null)
120 | Load(img, false);
121 | };
122 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
123 | bw.RunWorkerAsync();
124 | }
125 |
126 | private void OnContextMenuOpening(object sender, ContextMenuEventArgs e) {
127 | SaveIconContextMenuItem.IsEnabled = HaveIcon;
128 | SaveBannerContextMenuItem.IsEnabled = HaveBanner;
129 | }
130 |
131 | public byte[] GetData() { return _assetFile.FileData; }
132 | }
133 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/OnlineAssetsControl.xaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
27 |
28 | Internet Archive
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
60 |
62 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
86 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/ScreenshotsControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
21 |
23 |
25 |
28 |
29 |
30 |
32 |
33 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Controls/ScreenshotsControl.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // ScreenshotsControl.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor.Controls {
9 | using System;
10 | using System.ComponentModel;
11 | using System.Drawing.Imaging;
12 | using System.IO;
13 | using System.Linq;
14 | using System.Windows;
15 | using System.Windows.Controls;
16 | using System.Windows.Media.Imaging;
17 | using Classes;
18 | using Microsoft.Win32;
19 | using Image = System.Drawing.Image;
20 | using Size = System.Drawing.Size;
21 |
22 | ///
23 | /// Interaction logic for ScreenshotsControl.xaml
24 | ///
25 | public partial class ScreenshotsControl {
26 | private readonly MainWindow _main;
27 | internal bool HavePreview;
28 | private AuroraAsset.AssetFile _assetFile;
29 | private Image[] _screenshots;
30 |
31 | public ScreenshotsControl(MainWindow main) {
32 | InitializeComponent();
33 | _main = main;
34 | _assetFile = new AuroraAsset.AssetFile();
35 | _screenshots = new Image[AuroraAsset.AssetType.ScreenshotEnd - AuroraAsset.AssetType.ScreenshotStart];
36 | CBox.Items.Clear();
37 | for(var i = 0; i < _screenshots.Length; i++)
38 | CBox.Items.Add(new ScreenshotDisplay(i));
39 | CBox.SelectedIndex = 0;
40 | }
41 |
42 | public bool HaveScreenshots { get { return _screenshots.Any(t => t != null); } }
43 |
44 | public void Save() {
45 | var sfd = new SaveFileDialog();
46 | if(sfd.ShowDialog() != true)
47 | return;
48 | Save(sfd.FileName);
49 | }
50 |
51 | public void Save(string filename) {
52 | var index = 1;
53 | foreach(var img in _screenshots.Where(img => img != null)) { // Loop screenshots adding them in the order the user wants them with no interruption
54 | if(!img.Equals(_assetFile.GetScreenshot(index))) // Don't replace it if it's already where we want it to be
55 | _assetFile.SetScreenshot(img, index, _main.UseCompression.IsChecked); // Add screenshot with current settings
56 | index++; // Increment index so we put next image in next slot
57 | }
58 | if(index - 1 < _screenshots.Length) // Do we have any slots that are not used?
59 | {
60 | for(; index - 1 < _screenshots.Length; index++) // Loop remaining slots
61 | _assetFile.SetScreenshot(null, index, false); // Remove unused slots
62 | }
63 | File.WriteAllBytes(filename, _assetFile.FileData);
64 | }
65 |
66 | public void Reset() {
67 | SetPreview(null);
68 | _assetFile = new AuroraAsset.AssetFile();
69 | _screenshots = new Image[AuroraAsset.AssetType.ScreenshotEnd - AuroraAsset.AssetType.ScreenshotStart];
70 | CBox.SelectedIndex = 0;
71 | }
72 |
73 | public void Load(AuroraAsset.AssetFile asset) {
74 | _assetFile.SetScreenshots(asset);
75 | Dispatcher.Invoke(new Action(() => {
76 | _screenshots = _assetFile.GetScreenshots();
77 | CBox_SelectionChanged(null, null);
78 | }));
79 | }
80 |
81 | private void SetPreview(Image img) {
82 | if(img == null) {
83 | PreviewImg.Source = new BitmapImage(new Uri("pack://application:,,,/Resources/Placeholders/screenshot.png", UriKind.Absolute));
84 | HavePreview = false;
85 | return;
86 | }
87 | var bi = new BitmapImage();
88 | var ms = new MemoryStream();
89 | img.Save(ms, ImageFormat.Bmp);
90 | ms.Seek(0, SeekOrigin.Begin);
91 | bi.BeginInit();
92 | bi.StreamSource = ms;
93 | bi.EndInit();
94 | PreviewImg.Source = bi;
95 | HavePreview = true;
96 | }
97 |
98 | public void Load(Image img, bool replace) {
99 | var index = -1;
100 | if(replace) {
101 | var disp = CBox.SelectedItem as ScreenshotDisplay;
102 | if(disp == null) {
103 | for(var i = 0; i < _screenshots.Length; i++) {
104 | if(_screenshots[i] != null)
105 | continue;
106 | index = i;
107 | break;
108 | }
109 | }
110 | else
111 | index = disp.Index;
112 | }
113 | else {
114 | for(var i = 0; i < _screenshots.Length; i++) {
115 | if(_screenshots[i] != null)
116 | continue;
117 | index = i;
118 | break;
119 | }
120 | }
121 | if(index == -1) {
122 | MessageBox.Show("There is no space left for new screenshots :(", "No space left", MessageBoxButton.OK, MessageBoxImage.Error);
123 | return;
124 | }
125 | var shouldUseCompression = false;
126 | Dispatcher.Invoke(new Action(() => shouldUseCompression = _main.UseCompression.IsChecked));
127 | _assetFile.SetScreenshot(img, index + 1, shouldUseCompression);
128 | Dispatcher.Invoke(new Action(() => {
129 | _screenshots[index] = img;
130 | SetPreview(img);
131 | CBox.SelectedIndex = index;
132 | }));
133 | }
134 |
135 | public bool SelectedExists() {
136 | var disp = CBox.SelectedItem as ScreenshotDisplay;
137 | if(disp != null)
138 | return _screenshots[disp.Index] != null; // If true, we want to ask if we should add new one or replace existing
139 | return false;
140 | }
141 |
142 | public bool SpaceLeft() { return _screenshots.Any(t => t == null); }
143 |
144 | private void CBox_SelectionChanged(object sender, SelectionChangedEventArgs e) {
145 | var disp = CBox.SelectedItem as ScreenshotDisplay;
146 | if(disp == null)
147 | return;
148 | SetPreview(_screenshots[disp.Index]);
149 | }
150 |
151 | private void OnDragEnter(object sender, DragEventArgs e) { _main.OnDragEnter(sender, e); }
152 |
153 | private void OnDrop(object sender, DragEventArgs e) { _main.DragDrop(this, e); }
154 |
155 | internal void RemoveScreenshot(object sender, RoutedEventArgs e) { Load(null, true); }
156 |
157 | internal void SaveImageToFileOnClick(object sender, RoutedEventArgs e) {
158 | var disp = CBox.SelectedItem as ScreenshotDisplay;
159 | if(disp == null)
160 | return;
161 | MainWindow.SaveToFile(_screenshots[disp.Index], "Select where to save the Screenshot", string.Format("screenshot{0}.png", disp.Index + 1));
162 | }
163 |
164 | internal void SelectNewScreenshot(object sender, RoutedEventArgs e) {
165 | var bw = new BackgroundWorker();
166 | bw.DoWork += (o, args) => {
167 | var img = _main.LoadImage("Select new screenshot", "screenshot.png", new Size(1000, 562));
168 | if(img != null)
169 | Load(img, true);
170 | };
171 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
172 | bw.RunWorkerAsync();
173 | }
174 |
175 | internal void AddNewScreenshot(object sender, RoutedEventArgs e) {
176 | var bw = new BackgroundWorker();
177 | bw.DoWork += (o, args) => {
178 | var imglist = _main.LoadImages("Select new screenshot(s)", "screenshot.png", new Size(1000, 562));
179 | if(imglist == null)
180 | return;
181 | foreach(var img in imglist)
182 | Load(img, false);
183 | };
184 | bw.RunWorkerCompleted += (o, args) => _main.BusyIndicator.Visibility = Visibility.Collapsed;
185 | bw.RunWorkerAsync();
186 | }
187 |
188 | private void OnContextMenuOpening(object sender, ContextMenuEventArgs e) {
189 | SaveContextMenuItem.IsEnabled = HavePreview;
190 | RemoveContextMenuItem.IsEnabled = HavePreview;
191 | }
192 |
193 | public byte[] GetData() { return _assetFile.FileData; }
194 |
195 | private class ScreenshotDisplay {
196 | private readonly int _index;
197 |
198 | public ScreenshotDisplay(int index) { _index = index; }
199 |
200 | public int Index { get { return _index; } }
201 |
202 | public override string ToString() { return string.Format("Screenshot {0}", _index + 1); }
203 | }
204 | }
205 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/Helpers/GlobalState.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Windows;
3 | using AuroraAssetEditor.Models;
4 |
5 | namespace AuroraAssetEditor.Helpers
6 | {
7 | public static class GlobalState
8 | {
9 | private static Game _currentGame = new Game()
10 | {
11 | IsGameSelected=false
12 | };
13 |
14 | public static event Action GameChanged;
15 |
16 | public static Game CurrentGame
17 | {
18 | get => _currentGame;
19 | set
20 | {
21 | if (_currentGame != value)
22 | {
23 | _currentGame = value;
24 | GameChanged?.Invoke();
25 | }
26 | }
27 | }
28 | }
29 |
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/InputDialog.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Question:
13 | Value
14 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/InputDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // InputDialog.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 07/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor {
9 | using System;
10 | using System.Windows;
11 |
12 | public partial class InputDialog {
13 | public InputDialog(Window owner, string information, string defaultValue = "") {
14 | InitializeComponent();
15 | Icon = App.WpfIcon;
16 | Owner = owner;
17 | InfoLabel.Text = information;
18 | ValueBox.Text = defaultValue;
19 | }
20 |
21 | public string Value { get { return ValueBox.Text; } }
22 |
23 | private void btnDialogOk_Click(object sender, RoutedEventArgs e) { DialogResult = true; }
24 |
25 | private void Window_ContentRendered(object sender, EventArgs e) {
26 | ValueBox.SelectAll();
27 | ValueBox.Focus();
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/MainWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
69 |
73 |
74 |
75 |
76 |
77 |
80 |
83 |
84 |
85 |
88 |
89 |
90 |
91 |
96 |
97 |
98 |
99 |
100 |
105 |
106 |
107 |
108 |
109 |
110 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Models/Game.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace AuroraAssetEditor.Models
9 | {
10 | public class Game : INotifyPropertyChanged
11 | {
12 | private string _title;
13 | private string _titleId;
14 | private string _dbId;
15 | private bool _isGameSelected;
16 |
17 | public string Title
18 | {
19 | get => _title;
20 | set { _title = value; OnPropertyChanged(Title); }
21 | }
22 |
23 | public string TitleId
24 | {
25 | get => _titleId;
26 | set { _titleId = value; OnPropertyChanged(TitleId); }
27 | }
28 |
29 | public string DbId
30 | {
31 | get => _dbId;
32 | set { _dbId = value; OnPropertyChanged(DbId); }
33 | }
34 |
35 | public bool IsGameSelected
36 | {
37 | get => _isGameSelected;
38 | set { _isGameSelected = value; OnPropertyChanged(nameof(IsGameSelected)); }
39 | }
40 |
41 | public event PropertyChangedEventHandler PropertyChanged;
42 |
43 | protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null)
44 | {
45 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
46 | }
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/Placeholders/background.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/Placeholders/background.png
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/Placeholders/banner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/Placeholders/banner.png
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/Placeholders/cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/Placeholders/cover.png
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/Placeholders/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/Placeholders/icon.png
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/Placeholders/screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/Placeholders/screenshot.png
--------------------------------------------------------------------------------
/AuroraAssetEditor/Resources/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/Resources/icon.ico
--------------------------------------------------------------------------------
/AuroraAssetEditor/TitleAndDbIdDialog.xaml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
23 |
25 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/AuroraAssetEditor/TitleAndDbIdDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | //
2 | // TitleAndDbIdDialog.xaml.cs
3 | // AuroraAssetEditor
4 | //
5 | // Created by Swizzy on 10/05/2015
6 | // Copyright (c) 2015 Swizzy. All rights reserved.
7 |
8 | namespace AuroraAssetEditor {
9 | using System.Globalization;
10 | using System.Text.RegularExpressions;
11 | using System.Windows;
12 | using System.Windows.Controls;
13 | using System.Windows.Input;
14 | using AuroraAssetEditor.Helpers;
15 |
16 | public partial class TitleAndDbIdDialog {
17 | public TitleAndDbIdDialog(Window owner) {
18 | InitializeComponent();
19 | Icon = App.WpfIcon;
20 | Owner = owner;
21 | TitleIdBox.Text = GlobalState.CurrentGame.TitleId;
22 | DbIdBox.Text = GlobalState.CurrentGame.DbId;
23 | }
24 |
25 | public string TitleId { get { return TitleIdBox.Text; } }
26 |
27 | public string AssetId { get { return string.Format("{0}_{1}", TitleIdBox.Text, DbIdBox.Text); } }
28 |
29 | private void btnDialogOk_Click(object sender, RoutedEventArgs e) { DialogResult = true; }
30 |
31 | private void OnTextInput(object sender, TextCompositionEventArgs e) {
32 | uint tmp;
33 | e.Handled = !uint.TryParse(e.Text, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out tmp);
34 | }
35 |
36 | private void OnTextChanged(object sender, TextChangedEventArgs e) {
37 | TitleIdBox.Text = Regex.Replace(TitleIdBox.Text, "[^a-fA-F0-9]+", "");
38 | DbIdBox.Text = Regex.Replace(DbIdBox.Text, "[^a-fA-F0-9]+", "");
39 | OkButton.IsEnabled = TitleIdBox.Text.Length == 8 && DbIdBox.Text.Length == 8;
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/AuroraAssetEditor/msvcr100.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/AuroraAssetEditor/msvcr100.dll
--------------------------------------------------------------------------------
/CHANGELOG.txt:
--------------------------------------------------------------------------------
1 | v1.2
2 | ~ Fixed a bug with not overwriting the settings JSON file properly with FTP
3 | ~ Fixed a bug with not saving settings without first doing a connection test
4 | ~ Fixed a bug causing issues after ~ 10 minutes of doing nothing
5 | + Added support for keyword search on xbox.com, my observations are that it only searches titles starting with the words you write tho... the search is done on xbox.com, Many thanks to JQE for the support on this!
6 | ~ Fixed a bug causing the application to crash when there's no internet available
7 | - Removed advanced mode -> enabling the settings menu options by default as they're now supported fully (as of Aurora 0.6b they'll not cause any problems)
8 |
9 | v1.1
10 | + Added support for reading FSD Assets file(s) and converting to Aurora assets - Thanks for updating the dll to read dds MaesterRowen
11 | also thanks for helping me with the structure of these files, without you this app wouldn't have existed!
12 | + Added saving all assets in one go - Thanks for the suggestion felida!
13 | + Added support for downloading Assets from xbox.com and/or Xboxunity.net
14 | + Added an icon thanks to RAP7HOR
15 | + Moved all processing to a background thread and added a busy indicator to make the application not freeze and such...
16 | + Added FTP Support, you can now load/save your assets directly from your network using FTP (requires that you are actually running Aurora for it to work)
17 |
18 | v1.0
19 | + Initial release
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | This is free and unencumbered software released into the public domain.
2 |
3 | Anyone is free to copy, modify, publish, use, compile, sell, or
4 | distribute this software, either in source code form or as a compiled
5 | binary, for any purpose, commercial or non-commercial, and by any
6 | means.
7 |
8 | In jurisdictions that recognize copyright laws, the author or authors
9 | of this software dedicate any and all copyright interest in the
10 | software to the public domain. We make this dedication for the benefit
11 | of the public at large and to the detriment of our heirs and
12 | successors. We intend this dedication to be an overt act of
13 | relinquishment in perpetuity of all present and future rights to this
14 | software under copyright law.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 | OTHER DEALINGS IN THE SOFTWARE.
23 |
24 | For more information, please refer to
25 |
26 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AuroraAssetEditor
2 | A Graphical editor for Aurora's .asset files
3 |
4 | There will be more features added to this tool as i find the time to do so, first version only supports reading/writing Aurora .asset files
5 |
6 | [](https://ci.appveyor.com/project/swizzy/auroraasseteditor)
7 |
8 | A Huge thanks goes out to MaesterRowen for explaining the format for me, and also for making the AuroraAsset.dll library which handles the image conversion (between D3DTexture and Bitmap)
9 | Many thanks also goes to Mattie for the placeholder images :)
10 |
11 | Thanks also goes to gavin_darkglider & felida for helping me test this thing :)
12 |
13 | Thanks to RAP7HOR for the icon used in the application as of version 1.1!
14 |
--------------------------------------------------------------------------------
/SQLite.Interop/x86/SQLite.Interop.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/XboxUnity/AuroraAssetEditor/10dd5d94264887d1fed2023280a3b9c070bb88e0/SQLite.Interop/x86/SQLite.Interop.dll
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 1.4.{build}
2 | image: Visual Studio 2022
3 | skip_tags: true
4 | configuration: Release
5 |
6 | dotnet_csproj:
7 | patch: true
8 | file: '**\*.csproj'
9 | version: '{version}'
10 | file_version: '{version}'
11 | package_version: '{version}'
12 | assembly_version: '{version}'
13 | informational_version: '{version}'
14 |
15 | build_script:
16 | - cmd: dotnet restore
17 | - cmd: dotnet build -c Release
18 | - cmd: dotnet publish -c Release -o publish
19 | - cmd: xcopy C:\projects\auroraasseteditor\SQLite.Interop C:\projects\auroraasseteditor\publish /s
20 |
21 | artifacts:
22 | - path: publish
23 | name: AuroraAssetEditor
24 |
25 | deploy:
26 | - provider: GitHub
27 | tag: v$(APPVEYOR_BUILD_VERSION)
28 | release: Aurora Asset Editor v$(APPVEYOR_BUILD_VERSION)
29 | auth_token:
30 | secure: 1ugGk17lkq2gESnUH82Dt9Mfa1BQT3KUcqbgQHafLlFM9BOAwQMNUlj5h0jNhCwO
31 | artifact: AuroraAssetEditor
32 | draft: true
33 |
34 | #on_finish:
35 | # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
36 |
--------------------------------------------------------------------------------
/tools/ParseLocale.py:
--------------------------------------------------------------------------------
1 | import re
2 | import requests
3 |
4 |
5 | def parse_locales(file_data):
6 | locales = []
7 | for locale_name, locale_var in re.findall(
8 | r'{title:"([^"]+)",locale:([^}]+)}', file_data.decode("utf-8")
9 | ):
10 | locale_id = f"{locale_var[2:4]}-{locale_var[4:]}"
11 | locales.append((locale_id, locale_name))
12 | return locales
13 |
14 |
15 | def main():
16 | url = "https://assets-www.xbox.com/xbox-web/static/js/LocalePickerPage.7c45fcf5.chunk.js"
17 | resp = requests.get(url, timeout=60)
18 | resp.raise_for_status()
19 | for locale_id, locale_name in parse_locales(resp.content):
20 | print(f'ret.Add(new XboxLocale("{locale_id}", "{locale_name}"));')
21 |
22 |
23 | if __name__ == "__main__":
24 | main()
25 |
--------------------------------------------------------------------------------