├── .gitignore
├── LICENSE
├── SKIM.ico
├── SKIM.sln
├── backup.bmp
├── branch.bmp
├── config.bmp
├── donate.bmp
├── exit.bmp
├── folder.bmp
├── info.bmp
├── install.bmp
├── logs.bmp
├── menu.bmp
├── release_notes.bmp
├── src
├── APP_VERSION.H
├── DLC.cpp
├── SKIM.cpp
├── SKIM.filters
├── SKIM.h
├── SKIM.rc
├── SKIM.vcxproj
├── SKIM.vcxproj.filters
├── branch.h
├── ini.cpp
├── ini.h
├── injection.cpp
├── injection.h
├── network.cpp
├── network.h
├── product.h
├── resource.h
├── stdafx.cpp
├── stdafx.h
├── system_tray.cpp
├── system_tray.h
└── targetver.h
├── start.bmp
├── stop.bmp
├── tray.ico
├── uninstall.bmp
├── update.bmp
└── utilities.bmp
/.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 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.pch
53 | *.pdb
54 | *.pgc
55 | *.pgd
56 | *.rsp
57 | *.sbr
58 | *.tlb
59 | *.tli
60 | *.tlh
61 | *.tmp
62 | *.tmp_proj
63 | *.log
64 | *.vspscc
65 | *.vssscc
66 | .builds
67 | *.pidb
68 | *.svclog
69 | *.scc
70 |
71 | # Chutzpah Test files
72 | _Chutzpah*
73 |
74 | # Visual C++ cache files
75 | ipch/
76 | *.aps
77 | *.ncb
78 | *.opendb
79 | *.opensdf
80 | *.sdf
81 | *.cachefile
82 |
83 | # Visual Studio profiler
84 | *.psess
85 | *.vsp
86 | *.vspx
87 | *.sap
88 |
89 | # TFS 2012 Local Workspace
90 | $tf/
91 |
92 | # Guidance Automation Toolkit
93 | *.gpState
94 |
95 | # ReSharper is a .NET coding add-in
96 | _ReSharper*/
97 | *.[Rr]e[Ss]harper
98 | *.DotSettings.user
99 |
100 | # JustCode is a .NET coding add-in
101 | .JustCode
102 |
103 | # TeamCity is a build add-in
104 | _TeamCity*
105 |
106 | # DotCover is a Code Coverage Tool
107 | *.dotCover
108 |
109 | # NCrunch
110 | _NCrunch_*
111 | .*crunch*.local.xml
112 | nCrunchTemp_*
113 |
114 | # MightyMoose
115 | *.mm.*
116 | AutoTest.Net/
117 |
118 | # Web workbench (sass)
119 | .sass-cache/
120 |
121 | # Installshield output folder
122 | [Ee]xpress/
123 |
124 | # DocProject is a documentation generator add-in
125 | DocProject/buildhelp/
126 | DocProject/Help/*.HxT
127 | DocProject/Help/*.HxC
128 | DocProject/Help/*.hhc
129 | DocProject/Help/*.hhk
130 | DocProject/Help/*.hhp
131 | DocProject/Help/Html2
132 | DocProject/Help/html
133 |
134 | # Click-Once directory
135 | publish/
136 |
137 | # Publish Web Output
138 | *.[Pp]ublish.xml
139 | *.azurePubxml
140 | # TODO: Comment the next line if you want to checkin your web deploy settings
141 | # but database connection strings (with potential passwords) will be unencrypted
142 | *.pubxml
143 | *.publishproj
144 |
145 | # NuGet Packages
146 | *.nupkg
147 | # The packages folder can be ignored because of Package Restore
148 | **/packages/*
149 | # except build/, which is used as an MSBuild target.
150 | !**/packages/build/
151 | # Uncomment if necessary however generally it will be regenerated when needed
152 | #!**/packages/repositories.config
153 | # NuGet v3's project.json files produces more ignoreable files
154 | *.nuget.props
155 | *.nuget.targets
156 |
157 | # Microsoft Azure Build Output
158 | csx/
159 | *.build.csdef
160 |
161 | # Microsoft Azure Emulator
162 | ecf/
163 | rcf/
164 |
165 | # Microsoft Azure ApplicationInsights config file
166 | ApplicationInsights.config
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 |
172 | # Visual Studio cache files
173 | # files ending in .cache can be ignored
174 | *.[Cc]ache
175 | # but keep track of directories ending in .cache
176 | !*.[Cc]ache/
177 |
178 | # Others
179 | ClientBin/
180 | ~$*
181 | *~
182 | *.dbmdl
183 | *.dbproj.schemaview
184 | *.pfx
185 | *.publishsettings
186 | node_modules/
187 | orleans.codegen.cs
188 |
189 | # RIA/Silverlight projects
190 | Generated_Code/
191 |
192 | # Backup & report files from converting an old project file
193 | # to a newer Visual Studio version. Backup files are not needed,
194 | # because we have git ;-)
195 | _UpgradeReport_Files/
196 | Backup*/
197 | UpgradeLog*.XML
198 | UpgradeLog*.htm
199 |
200 | # SQL Server files
201 | *.mdf
202 | *.ldf
203 |
204 | # Business Intelligence projects
205 | *.rdl.data
206 | *.bim.layout
207 | *.bim_*.settings
208 |
209 | # Microsoft Fakes
210 | FakesAssemblies/
211 |
212 | # GhostDoc plugin setting file
213 | *.GhostDoc.xml
214 |
215 | # Node.js Tools for Visual Studio
216 | .ntvs_analysis.dat
217 |
218 | # Visual Studio 6 build log
219 | *.plg
220 |
221 | # Visual Studio 6 workspace options file
222 | *.opt
223 |
224 | # Visual Studio LightSwitch build output
225 | **/*.HTMLClient/GeneratedArtifacts
226 | **/*.DesktopClient/GeneratedArtifacts
227 | **/*.DesktopClient/ModelManifest.xml
228 | **/*.Server/GeneratedArtifacts
229 | **/*.Server/ModelManifest.xml
230 | _Pvt_Extensions
231 |
232 | # Paket dependency manager
233 | .paket/paket.exe
234 |
235 | # FAKE - F# Make
236 | .fake/
237 | /SKIM.VC.db
238 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/SKIM.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/SKIM.ico
--------------------------------------------------------------------------------
/SKIM.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 14
4 | VisualStudioVersion = 14.0.25420.1
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SKIM", "src\SKIM.vcxproj", "{1AF98DCE-F41F-411F-956E-6056EA1A282C}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B4BF2D2A-C38B-4D00-8DCB-842ED361DCF9}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|x64 = Debug|x64
13 | Debug|x86 = Debug|x86
14 | Release|x64 = Release|x64
15 | Release|x86 = Release|x86
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Debug|x64.ActiveCfg = Debug|x64
19 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Debug|x64.Build.0 = Debug|x64
20 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Debug|x86.ActiveCfg = Debug|Win32
21 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Debug|x86.Build.0 = Debug|Win32
22 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Release|x64.ActiveCfg = Release|x64
23 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Release|x64.Build.0 = Release|x64
24 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Release|x86.ActiveCfg = Release|Win32
25 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}.Release|x86.Build.0 = Release|Win32
26 | EndGlobalSection
27 | GlobalSection(SolutionProperties) = preSolution
28 | HideSolutionNode = FALSE
29 | EndGlobalSection
30 | EndGlobal
31 |
--------------------------------------------------------------------------------
/backup.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/backup.bmp
--------------------------------------------------------------------------------
/branch.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/branch.bmp
--------------------------------------------------------------------------------
/config.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/config.bmp
--------------------------------------------------------------------------------
/donate.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/donate.bmp
--------------------------------------------------------------------------------
/exit.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/exit.bmp
--------------------------------------------------------------------------------
/folder.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/folder.bmp
--------------------------------------------------------------------------------
/info.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/info.bmp
--------------------------------------------------------------------------------
/install.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/install.bmp
--------------------------------------------------------------------------------
/logs.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/logs.bmp
--------------------------------------------------------------------------------
/menu.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/menu.bmp
--------------------------------------------------------------------------------
/release_notes.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/release_notes.bmp
--------------------------------------------------------------------------------
/src/APP_VERSION.H:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 |
4 | #define SKIM_MAJOR 0
5 | #define SKIM_MINOR 7
6 | #define SKIM_BUILD 5
7 | #define SKIM_REV 14
8 |
9 |
10 |
11 |
12 |
13 | #define _A2(a) #a
14 | #define _A(a) _A2(a)
15 | #define _L2(w) L ## w
16 | #define _L(w) _L2(w)
17 |
18 |
19 | #if (SKIM_REV > 0)
20 | #define SKIM_VERSION_STR_A _A(SKIM_MAJOR) "." _A(SKIM_MINOR) "." _A(SKIM_BUILD) "." _A(SKIM_REV)
21 | #else
22 | #define SKIM_VERSION_STR_A _A(SKIM_MAJOR) "." _A(SKIM_MINOR) "." _A(SKIM_BUILD)
23 | #endif
24 |
25 | #define SKIM_VERSION_STR_W _L(SKIM_VERSION_STR_A)
26 |
27 |
28 | #define SKIM_FILE_VERSION SKIM_MAJOR,SKIM_MINOR,SKIM_BUILD,SKIM_REV
29 | #define SKIM_PRODUCT_VERSION SKIM_MAJOR,SKIM_MINOR,SKIM_BUILD,SKIM_REV
30 |
--------------------------------------------------------------------------------
/src/SKIM.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/src/SKIM.cpp
--------------------------------------------------------------------------------
/src/SKIM.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 |
18 |
19 | Header Files
20 |
21 |
22 | Header Files
23 |
24 |
25 | Header Files
26 |
27 |
28 | Header Files
29 |
30 |
31 |
32 |
33 | Source Files
34 |
35 |
36 | Source Files
37 |
38 |
39 | Source Files
40 |
41 |
42 |
43 |
44 | Resource Files
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/SKIM.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // dllmain.cpp : Defines the entry point for the DLL application.
4 | #define _CRT_SECURE_NO_WARNINGS
5 | #define _CRT_NON_CONFORMING_WCSTOK
6 | #define _CRT_NON_CONFORMING_SWPRINTFS
7 |
8 | #pragma warning (disable: 4091)
9 |
10 | #include "stdafx.h"
11 | #include
12 |
13 | extern HINSTANCE g_hInstance;
14 |
15 | //bool child = false;
16 | //wchar_t startup_dir [MAX_PATH + 1] = { };
17 |
18 | #include "product.h"
19 | #include "branch.h"
20 |
21 | const wchar_t* SKIM_SteamUtil_GetSteamIntallDir (void);
22 | const wchar_t* SKIM_SteamUtil_FindAppInstallPath (uint32_t appid);
23 |
24 | bool SKIM_Depends_TestKB2533623 (SK_ARCHITECTURE arch);
25 | bool SKIM_Depends_TestVisualCRuntime (SK_ARCHITECTURE arch);
26 |
27 | bool SKIM_Util_IsAdmin (void);
28 | bool SKIM_Util_IsProcessRunning (const wchar_t* wszProcName);
29 | bool SKIM_Util_IsDirectory (const wchar_t* wszPath);
30 | bool SKIM_Util_IsDLLFromProduct (const wchar_t* wszName, const wchar_t* wszProductName);
31 |
32 | bool SKIM_Util_CreateDirectories (const wchar_t* wszPath);
33 | bool SKIM_Util_GetDocumentsDir (wchar_t* buf, uint32_t* pdwLen);
34 | void SKIM_Util_MoveFileNoFail (const wchar_t* wszOld, const wchar_t* wszNew);
35 | void SKIM_Util_DeleteConfigFiles (sk_product_t* product);
36 |
37 |
38 | // Threads
39 | unsigned int __stdcall SKIM_MigrateProduct (LPVOID user);
40 | unsigned int __stdcall SKIM_InstallProduct (LPVOID user);
41 | unsigned int __stdcall SKIM_UninstallProduct (LPVOID user);
42 | unsigned int __stdcall SKIM_UpdateProduct (LPVOID user);
43 |
44 | DWORD SKIM_CountProductBranches (sk_product_t *pProduct);
45 | SKIM_BranchManager::Branch* SKIM_GetProductBranchByIdx (sk_product_t *pProduct, int idx);
46 | bool SKIM_DetermineInstallState (sk_product_t& product);
47 | std::wstring SKIM_SummarizeRenderAPI (sk_product_t& product);
48 |
49 | int SKIM_GetProductIdx (sk_product_t* prod);
50 | sk_product_t* SKIM_GetProductByIdx (int idx);
51 | std::vector SKIM_GetInstallableProducts (void);
52 |
53 | RECT SKIM_GetHWNDRect (HWND hWnd);
54 | RECT SKIM_GetClientRect (HWND hWnd);
55 | RECT SKIM_GetDlgItemRect (HWND hDlg, UINT nIDDlgItem);
56 |
57 | void SKIM_OnBranchSelect (void);
58 | void SKIM_OnProductSelect (void);
59 | bool SKIM_ConfirmClose (void);
60 | void SKIM_Exit (void);
61 |
62 | enum {
63 | SKIM_STOP_INJECTION = WM_USER + 0x122,
64 | SKIM_STOP_INJECTION_AND_EXIT = WM_USER + 0x123,
65 | SKIM_START_INJECTION = WM_USER + 0x124,
66 | SKIM_RESTART_INJECTION = WM_USER + 0x125
67 | };
68 |
69 | constexpr uint8_t
70 | __stdcall
71 | SKIM_GetBitness (void)
72 | {
73 | #ifdef _WIN64
74 | return 64;
75 | #else
76 | return 32;
77 | #endif
78 | }
79 |
80 | #define SKIM_RunOnce(x) { static bool first = true; if (first) { (x); first = false; } }
81 |
82 | #define SKIM_RunIf32Bit(x) { SKIM_GetBitness () == 32 ? (x) : 0; }
83 | #define SKIM_RunIf64Bit(x) { SKIM_GetBitness () == 64 ? (x) : 0; }
84 | #define SKIM_RunLHIfBitness(b,l,r) SKIM_GetBitness () == (b) ? (l) : (r)
85 |
86 | //
87 | // PRIVATE
88 | //
89 | INT_PTR
90 | CALLBACK
91 | Main_DlgProc (
92 | _In_ HWND hWndDlg,
93 | _In_ UINT uMsg,
94 | _In_ WPARAM wParam,
95 | _In_ LPARAM lParam );
96 |
97 | sk_product_t* SKIM_FindProductByAppID (uint32_t appid);
98 | void SKIM_DisableGlobalInjector (void);
99 |
100 |
101 | unsigned int __stdcall SKIM_MigrateGlobalInjector (LPVOID user);
102 | unsigned int __stdcall SKIM_InstallGlobalInjector (LPVOID user);
--------------------------------------------------------------------------------
/src/SKIM.rc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/src/SKIM.rc
--------------------------------------------------------------------------------
/src/SKIM.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | Win32
7 |
8 |
9 | Release
10 | Win32
11 |
12 |
13 | Debug
14 | x64
15 |
16 |
17 | Release
18 | x64
19 |
20 |
21 |
22 | {1AF98DCE-F41F-411F-956E-6056EA1A282C}
23 | Win32Proj
24 | SKIM
25 | 7.0
26 |
27 |
28 |
29 | Application
30 | true
31 | v141
32 | Unicode
33 |
34 |
35 | Application
36 | false
37 | v141_xp
38 | true
39 | Unicode
40 | false
41 |
42 |
43 | Application
44 | true
45 | v141_xp
46 | Unicode
47 |
48 |
49 | Application
50 | false
51 | v141_xp
52 | true
53 | Unicode
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | true
75 | false
76 | true
77 | C:\Program Files (x86)\Steam\SteamApps\common\Tales of Symphonia\
78 | skim
79 | .exe
80 |
81 |
82 | true
83 | C:\Users\andon\Documents\My Mods\SpecialK\
84 | .exe
85 | $(ProjectName)64
86 |
87 |
88 | false
89 | true
90 | true
91 | C:\Users\andon\Documents\My Mods\SpecialK\
92 | SKIM
93 | .exe
94 | AllRules.ruleset
95 |
96 |
97 | false
98 | .exe
99 | SKIM64
100 | C:\Users\andon\Documents\My Mods\SpecialK\
101 |
102 |
103 |
104 |
105 |
106 | Level3
107 | Disabled
108 | WIN32;_DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
109 | false
110 | false
111 | SyncCThrow
112 | Size
113 | true
114 | false
115 | MultiThreadedDebug
116 | false
117 | true
118 | StreamingSIMDExtensions2
119 | true
120 | Disabled
121 | false
122 |
123 |
124 | Windows
125 | Debug
126 | kernel32.lib
127 | NotSet
128 |
129 | RequireAdministrator
130 | true
131 | true
132 | true
133 | true
134 | UseFastLinkTimeCodeGeneration
135 | false
136 | false
137 | true
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 | Level3
148 | Disabled
149 | _DEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
150 | false
151 | false
152 | OnlyExplicitInline
153 | true
154 | Speed
155 | false
156 | false
157 | false
158 | Fast
159 | false
160 | Default
161 |
162 |
163 | Windows
164 | true
165 | false
166 | false
167 | false
168 | false
169 |
170 |
171 |
172 |
173 | Level4
174 |
175 |
176 | MinSpace
177 | false
178 | false
179 | false
180 | false
181 | Async
182 | Size
183 | true
184 | false
185 | MultiThreaded
186 | true
187 | true
188 | false
189 | StreamingSIMDExtensions2
190 | Default
191 | false
192 |
193 |
194 | Windows
195 | true
196 | true
197 | false
198 | kernel32.lib
199 | NotSet
200 |
201 | AsInvoker
202 | false
203 | false
204 | false
205 | false
206 | false
207 | false
208 | MachineX86
209 | 6.1
210 | true
211 | false
212 |
213 |
214 | /pdbstripped:skim.pdb %(AdditionalOptions)
215 | false
216 | true
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 | Level4
225 |
226 |
227 | MinSpace
228 | false
229 | false
230 | _WIN64;NDEBUG;_WINDOWS;_USRDLL;%(PreprocessorDefinitions);
231 | Default
232 | Size
233 | false
234 | false
235 | MultiThreaded
236 | true
237 | false
238 | false
239 | true
240 | Fast
241 | false
242 | stdcpp14
243 | true
244 | true
245 | Async
246 | false
247 |
248 |
249 | Windows
250 | true
251 | true
252 | false
253 |
254 |
255 | kernel32.lib
256 | false
257 | true
258 | false
259 | false
260 | false
261 | false
262 | false
263 | true
264 | 6.1
265 | UseLinkTimeCodeGeneration
266 |
267 |
268 | true
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 | false
291 |
292 |
293 | false
294 |
295 |
296 | false
297 |
298 |
299 | false
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
--------------------------------------------------------------------------------
/src/SKIM.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | DLC
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | {09b3bea6-2a10-45ca-af8a-dbe9d5fe31d4}
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/src/branch.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "ini.h"
6 |
7 | extern const wchar_t*
8 | SKIM_FindInstallPath (uint32_t appid);
9 |
10 | iSK_INI*
11 | __stdcall
12 | SK_CreateINI (const wchar_t* const wszName);
13 |
14 | class SKIM_BranchManager
15 | {
16 | public:
17 | class Branch
18 | {
19 | public:
20 | std::wstring description;
21 | std::wstring name;
22 | };
23 |
24 | SKIM_BranchManager (void) {
25 | product_.reset ();
26 | }
27 |
28 | ~SKIM_BranchManager (void) {
29 | }
30 |
31 | static SKIM_BranchManager* singleton (void)
32 | {
33 | static SKIM_BranchManager* pMgr = nullptr;
34 |
35 | if (pMgr == nullptr)
36 | pMgr = new SKIM_BranchManager ();
37 |
38 | return pMgr;
39 | }
40 |
41 | uint32_t getProduct (void)
42 | {
43 | return product_.app_id;
44 | }
45 |
46 | void setProduct (uint32_t uiAppID)
47 | {
48 | if (product_.app_id == uiAppID)
49 | return;
50 |
51 | product_.app_id = uiAppID;
52 | product_.reset ();
53 | }
54 |
55 | std::wstring getInstallPackage (void)
56 | {
57 | return product_.install_package;
58 | }
59 |
60 | Branch* getBranch (const wchar_t* wszName)
61 | {
62 | return product_.getBranch (wszName);
63 | }
64 |
65 | Branch* getCurrentBranch (void) const
66 | {
67 | static Branch dummy = { L"Main", L"Main" };
68 |
69 | return product_.getCurrentBranch () ? product_.getCurrentBranch () :
70 | &dummy;
71 | }
72 |
73 | Branch* getBranchByIndex (uint32_t idx)
74 | {
75 | if (getNumberOfBranches () > idx)
76 | return &product_.branches [idx];
77 |
78 | return nullptr;
79 | }
80 |
81 | uint32_t getNumberOfBranches (void)
82 | {
83 | return product_.getCount ();
84 | }
85 |
86 | bool migrateToBranch (const wchar_t* wszName)
87 | {
88 | return product_.migrateToBranch (wszName);
89 | }
90 |
91 | protected:
92 | struct branch_list_s
93 | {
94 | void reset (void)
95 | {
96 | active_branch = nullptr;
97 | branches.clear ();
98 |
99 | if (installed_.ini != nullptr)
100 | {
101 | //delete installed_.ini;
102 | installed_.ini = nullptr;
103 | }
104 |
105 | if (repo_.ini != nullptr)
106 | {
107 | //delete repo_.ini;
108 | repo_.ini = nullptr;
109 | }
110 |
111 | if (app_id != UINT32_MAX)
112 | {
113 | const wchar_t* wszInstallPath =
114 | SKIM_FindInstallPath (app_id);
115 |
116 | wchar_t wszInstalledINI [MAX_PATH] = { L'\0' };
117 | wchar_t wszRepoINI [MAX_PATH] = { L'\0' };
118 |
119 | lstrcatW (wszInstalledINI, wszInstallPath);
120 | lstrcatW (wszRepoINI, wszInstallPath);
121 |
122 | lstrcatW (wszInstalledINI, L"\\Version\\installed.ini");
123 | lstrcatW (wszRepoINI, L"\\Version\\repository.ini");
124 |
125 | installed_.path = wszInstalledINI;
126 | repo_.path = wszRepoINI;
127 |
128 | installed_.ini = SK_CreateINI (installed_.path.c_str ());
129 | repo_.ini = SK_CreateINI (repo_.path.c_str ());
130 |
131 | auto repo_it =
132 | repo_.ini->get_sections ().begin ();
133 |
134 | while (repo_it != repo_.ini->get_sections ().end ())
135 | {
136 | // Find any section named Version.*
137 | if ( wcsstr (repo_it->second.name.c_str (), L"Version.") ==
138 | repo_it->second.name.c_str () )
139 | {
140 | Branch branch;
141 |
142 | branch.name =
143 | CharNextW (StrStrW (repo_it->second.name.c_str (), L"."));
144 |
145 | if (branch.name == L"Latest" || branch.name == L"Invalid")
146 | branch.name = L"Main";
147 |
148 | branch.description =
149 | const_cast (
150 | repo_it->second
151 | ).get_value (L"BranchDescription");
152 |
153 | if (branch.description == L"Invalid")
154 | branch.description = L"No Description Available?!";
155 |
156 | branches.push_back (branch);
157 | }
158 |
159 | ++repo_it;
160 | }
161 |
162 | std::wstring installed_branch =
163 | installed_.ini->get_section (L"Version.Local").get_value (L"Branch");
164 |
165 | install_package =
166 | installed_.ini->get_section (L"Version.Local").get_value (L"InstallPackage");
167 |
168 | if (installed_branch == L"Latest" || installed_branch == L"Invalid")
169 | installed_branch = L"Main";
170 |
171 | active_branch =
172 | getBranch (installed_branch.c_str ());
173 | }
174 | }
175 |
176 | Branch* getBranch (const wchar_t* wszName)
177 | {
178 | int idx = 0;
179 | auto it = branches.begin ();
180 |
181 | while (it != branches.end ())
182 | {
183 | if (! _wcsicmp (wszName, it->name.c_str ()))
184 | return &(*it);
185 |
186 | ++idx, ++it;
187 | }
188 |
189 | return nullptr;
190 | }
191 |
192 | Branch* getCurrentBranch (void) const
193 | {
194 | return active_branch;
195 | }
196 |
197 | uint32_t getCount (void) {
198 | return (uint32_t)branches.size ();
199 | }
200 |
201 | bool migrateToBranch (const wchar_t* wszName)
202 | {
203 | Branch* pBranch =
204 | getBranch (wszName);
205 |
206 | if (! pBranch)
207 | return false;
208 |
209 | if (active_branch == pBranch)
210 | return false;
211 |
212 | try
213 | {
214 | iSK_INI* installed =
215 | installed_.ini;
216 |
217 | iSK_INISection& sec =
218 | installed->get_section (L"Version.Local");
219 |
220 | if (! sec.contains_key (L"Branch"))
221 | sec.add_key_value (L"Branch", wszName);
222 |
223 |
224 | std::wstring& ini_branch =
225 | sec.get_value (L"Branch");
226 |
227 |
228 | // A little bit of trickery since I stupidly reserved the
229 | // Latest branch before I realized what that implied
230 | if (! wcscmp (wszName, L"Main"))
231 | ini_branch = L"Latest";
232 | else
233 | ini_branch = wszName;
234 |
235 | sec.remove_key (L"InstallPackage");
236 | sec.add_key_value (L"InstallPackage", L"PendingBranchMigration,0");
237 |
238 |
239 | iSK_INISection& update_sec =
240 | installed->get_section (L"Update.User");
241 |
242 | if (update_sec.contains_key (L"Reminder"))
243 | update_sec.get_value (L"Reminder") = L"0";
244 | else
245 | update_sec.add_key_value (L"Reminder", L"0");
246 |
247 | installed->write (installed_.path.c_str ());
248 | }
249 |
250 | catch (...) {
251 | return false;
252 | }
253 |
254 | return true;
255 | }
256 |
257 | std::wstring install_package = L"";
258 | Branch* active_branch = nullptr;
259 | std::vector branches;
260 | uint32_t app_id = 0;
261 |
262 | private:
263 | struct {
264 | iSK_INI* ini = nullptr;
265 | std::wstring path = L"";
266 | } installed_,
267 | repo_;
268 | } product_;
269 | };
--------------------------------------------------------------------------------
/src/ini.cpp:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Special K.
3 | *
4 | * Special K is free software : you can redistribute it
5 | * and/or modify it under the terms of the GNU General Public License
6 | * as published by The Free Software Foundation, either version 3 of
7 | * the License, or (at your option) any later version.
8 | *
9 | * Special K is distributed in the hope that it will be useful,
10 | *
11 | * But WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with Special K.
17 | *
18 | * If not, see .
19 | *
20 | **/
21 | #define _CRT_SECURE_NO_WARNINGS
22 | #define _CRT_NON_CONFORMING_SWPRINTFS
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | #include "stdafx.h"
29 |
30 | #include
31 | #include
32 |
33 | #include "ini.h"
34 |
35 | std::wstring
36 | ErrorMessage (errno_t err,
37 | const char* args,
38 | const wchar_t* ini_name,
39 | UINT line_no,
40 | const char* function_name,
41 | const char* file_name)
42 | {
43 | wchar_t wszFormattedError [1024];
44 |
45 | *wszFormattedError = L'\0';
46 |
47 | swprintf ( wszFormattedError, 1024,
48 | L"\n"
49 | L"Line %u of %hs (in %hs (...)):\n"
50 | L"------------------------\n\n"
51 | L"%hs\n\n File: %s\n\n"
52 | L"\t>> %s <<",
53 | line_no,
54 | file_name,
55 | function_name,
56 | args,
57 | ini_name,
58 | _wcserror (err) );
59 |
60 | return wszFormattedError;\
61 | }
62 |
63 | #define TRY_FILE_IO(x,y,z) { (z) = ##x; }
64 |
65 | uint64_t
66 | __stdcall
67 | SK_GetFileSize (const wchar_t* wszFile)
68 | {
69 | WIN32_FILE_ATTRIBUTE_DATA
70 | file_attrib_data = { 0 };
71 |
72 | if ( GetFileAttributesEx ( wszFile,
73 | GetFileExInfoStandard,
74 | &file_attrib_data ) )
75 | {
76 | return ULARGE_INTEGER { file_attrib_data.nFileSizeLow,
77 | file_attrib_data.nFileSizeHigh }.QuadPart;
78 | }
79 |
80 | return 0ULL;
81 | }
82 |
83 | bool
84 | SK_CreateDirectories ( const wchar_t* wszPath )
85 | {
86 | wchar_t* wszSubDir = new wchar_t [wcslen (wszPath) + 4] { };
87 | wcscpy (wszSubDir, wszPath);
88 | wchar_t* wszLastSlash = wcsrchr (wszSubDir, L'/');
89 | wchar_t* wszLastBackslash = wcsrchr (wszSubDir, L'\\');
90 |
91 | wchar_t* iter = nullptr;
92 |
93 | if (wszLastSlash > wszLastBackslash)
94 | *wszLastSlash = L'\0';
95 | else if (wszLastBackslash != nullptr)
96 | *wszLastBackslash = L'\0';
97 | else
98 | {
99 | delete [] wszSubDir;
100 | return false;
101 | }
102 |
103 | for (iter = wszSubDir; *iter != L'\0'; iter = CharNextW (iter))
104 | {
105 | if (*iter == L'\\' || *iter == L'/')
106 | {
107 | *iter = L'\0';
108 |
109 | if (GetFileAttributes (wszPath) == INVALID_FILE_ATTRIBUTES)
110 | CreateDirectoryW (wszSubDir, nullptr);
111 |
112 | *iter = L'\\';
113 | }
114 |
115 | // The final subdirectory (FULL PATH)
116 | if (GetFileAttributes (wszPath) == INVALID_FILE_ATTRIBUTES)
117 | CreateDirectoryW (wszSubDir, nullptr);
118 | }
119 |
120 | delete [] wszSubDir;
121 |
122 | return true;
123 | }
124 |
125 | iSK_INI::iSK_INI (const wchar_t* filename)
126 | {
127 | encoding_ = INI_UTF8;
128 |
129 | if (wcsstr (filename, L"Version"))
130 | SK_CreateDirectories (filename);
131 |
132 | sections.clear ();
133 |
134 | // We skip a few bytes (Unicode BOM) in certain circumstances, so this is the
135 | // actual pointer we need to free...
136 | wchar_t* alloc = nullptr;
137 |
138 | wszName =
139 | new wchar_t [wcslen (filename) + 2] { };
140 |
141 | wcscpy (wszName, filename);
142 |
143 | TRY_FILE_IO (_wfsopen (filename, L"rbS", _SH_DENYNO), filename, fINI);
144 |
145 | if (fINI != nullptr)
146 | {
147 | auto size =
148 | static_cast (SK_GetFileSize (filename));
149 |
150 | wszData = new wchar_t [size + 2] { };
151 | alloc = wszData;
152 |
153 | fread (wszData, size, 1, fINI);
154 | fflush (fINI);
155 | fclose (fINI);
156 |
157 | // First, consider Unicode
158 | // UTF16-LE (All is well in the world)
159 | if (*wszData == 0xFEFF)
160 | {
161 | ++wszData; // Skip the BOM
162 |
163 | encoding_ = INI_UTF16LE;
164 | }
165 |
166 | // UTF16-BE (Somehow we are swapped)
167 | else if (*wszData == 0xFFFE)
168 | {
169 | //dll_log.Log ( L"[INI Parser] Encountered Byte-Swapped Unicode INI "
170 | // L"file ('%s'), attempting to recover...",
171 | // wszName );
172 |
173 | wchar_t* wszSwapMe = wszData;
174 |
175 | for (int i = 0; i < size; i += 2)
176 | {
177 | *wszSwapMe++ =
178 | _byteswap_ushort (*wszSwapMe);
179 | }
180 |
181 | ++wszData; // Skip the BOM
182 |
183 | encoding_ = INI_UTF16BE;
184 | }
185 |
186 | // Something else, if it's ANSI or UTF-8, let's hope Windows can figure
187 | // out what to do...
188 | else
189 | {
190 | // Skip the silly UTF8 BOM if it is present
191 | bool utf8 = (reinterpret_cast (wszData)) [0] == 0xEF &&
192 | (reinterpret_cast (wszData)) [1] == 0xBB &&
193 | (reinterpret_cast (wszData)) [2] == 0xBF;
194 |
195 | const uintptr_t offset =
196 | utf8 ? 3 : 0;
197 |
198 | const int real_size =
199 | size - static_cast (offset);
200 |
201 | char* start_addr =
202 | (reinterpret_cast (wszData)) + offset;
203 |
204 | auto* string =
205 | new char [real_size];
206 |
207 | memcpy (string, start_addr, real_size);
208 |
209 | delete [] wszData;
210 |
211 | int converted_size =
212 | MultiByteToWideChar ( CP_UTF8, 0, string, real_size, nullptr, 0 );
213 |
214 | if (! converted_size)
215 | {
216 | //dll_log.Log ( L"[INI Parser] Could not convert UTF-8 / ANSI Encoded "
217 | // L".ini file ('%s') to UTF-16, aborting!",
218 | // wszName );
219 | wszData = nullptr;
220 |
221 | delete [] string;
222 |
223 | return;
224 | }
225 |
226 | wszData =
227 | new wchar_t [converted_size + 1] { };
228 |
229 | MultiByteToWideChar ( CP_UTF8, 0, string, real_size, wszData, converted_size );
230 |
231 | //dll_log.Log ( L"[INI Parser] Converted UTF-8 INI File: '%s'",
232 | //wszName );
233 |
234 | delete [] string;
235 |
236 | // No Byte-Order Marker
237 | alloc = wszData;
238 |
239 | encoding_ = INI_UTF8;
240 | }
241 |
242 | parse ();
243 |
244 | // TODO: Should we keep data in unparsed format?
245 | // delete [] alloc;
246 | // wszData = nullptr;
247 | // Why not? It would make re-parsing safer
248 | }
249 |
250 | else
251 | {
252 | wszData = nullptr;
253 | }
254 | }
255 |
256 | iSK_INI::~iSK_INI (void)
257 | {
258 | if (Release () == 0)
259 | {
260 | if (wszName != nullptr)
261 | {
262 | delete [] wszName;
263 | wszName = nullptr;
264 | }
265 |
266 | if (wszData != nullptr)
267 | {
268 | delete[] wszData;
269 | wszData = nullptr;
270 | }
271 | }
272 | }
273 |
274 | auto wcrlen =
275 | [](wchar_t *_start, wchar_t *_end) ->
276 | size_t
277 | {
278 | size_t _len = 0;
279 |
280 | wchar_t* _it = _start;
281 | while (_it < _end)
282 | {
283 | _it = CharNextW (_it);
284 | ++_len;
285 | }
286 |
287 | return _len;
288 | };
289 |
290 | iSK_INISection
291 | Process_Section (wchar_t* name, wchar_t* start, wchar_t* end)
292 | {
293 | iSK_INISection section (name);
294 |
295 | const wchar_t* penultimate = CharPrevW (start, end);
296 | wchar_t* key = start;
297 |
298 | for (wchar_t* k = key; k < end; k = CharNextW (k))
299 | {
300 | if (k < penultimate && *k == L'=')
301 | {
302 | auto* key_str = new wchar_t [k - key + 1] { };
303 | size_t key_len = wcrlen (key, k);
304 | wcsncpy (key_str, key, key_len);
305 |
306 | wchar_t* value =
307 | CharNextW (k);
308 |
309 | for (wchar_t* l = value; l <= end; l = CharNextW (l))
310 | {
311 | if (l > penultimate || *l == L'\n')
312 | {
313 | key = CharNextW (l);
314 | k = key;
315 |
316 | if (l == end)
317 | {
318 | l = CharNextW (l);
319 | k = end;
320 | }
321 |
322 | auto* val_str = new wchar_t [l - value + 1] { };
323 | size_t val_len = wcrlen (value, l);
324 | wcsncpy (val_str, value, val_len);
325 |
326 | section.add_key_value (key_str, val_str);
327 |
328 | delete [] val_str;
329 |
330 | l = end + 1;
331 | }
332 | }
333 |
334 | delete [] key_str;
335 | }
336 | }
337 |
338 | return section;
339 | }
340 |
341 | bool
342 | Import_Section (iSK_INISection& section, wchar_t* start, wchar_t* end)
343 | {
344 | const wchar_t* penultimate = CharPrevW (start, end);
345 | wchar_t* key = start;
346 |
347 | for (wchar_t* k = key; k < end; k = CharNextW (k))
348 | {
349 | if (k < penultimate && *k == L'=')
350 | {
351 | auto* key_str = new wchar_t [k - key + 1] { };
352 | size_t key_len = wcrlen (key, k);
353 | wcsncpy (key_str, key, key_len);
354 |
355 | wchar_t* value =
356 | CharNextW (k);
357 |
358 | for (wchar_t* l = value; l <= end; l = CharNextW (l))
359 | {
360 | if (l > penultimate || *l == L'\n')
361 | {
362 | key = CharNextW (l);
363 | k = key;
364 |
365 | if (l == end)
366 | {
367 | l = CharNextW (l);
368 | k = end;
369 | }
370 |
371 | auto* val_str = new wchar_t [l - value + 1] { };
372 | size_t val_len = wcrlen (value, l);
373 | wcsncpy (val_str, value, val_len);
374 |
375 | // Prefer to change an existing value
376 | if (section.contains_key (key_str))
377 | {
378 | std::wstring& val =
379 | section.get_value (key_str);
380 |
381 | val = val_str;
382 | }
383 |
384 | // But create a new one if it doesn't already exist
385 | else {
386 | section.add_key_value (key_str, val_str);
387 | }
388 |
389 | delete [] val_str;
390 |
391 | l = end + 1;
392 | }
393 | }
394 |
395 | delete [] key_str;
396 | }
397 | }
398 |
399 | return true;
400 | }
401 |
402 | void
403 | __stdcall
404 | iSK_INI::parse (void)
405 | {
406 | if (wszData != nullptr)
407 | {
408 | int len =
409 | lstrlenW (wszData);
410 |
411 | // We don't want CrLf, just Lf
412 | bool strip_cr = false;
413 |
414 | wchar_t* wszStrip =
415 | &wszData [0];
416 |
417 | // Find if the file has any Cr's
418 | for (int i = 0; i < len; i++)
419 | {
420 | if (*wszStrip == L'\r')
421 | {
422 | strip_cr = true;
423 | break;
424 | }
425 |
426 | wszStrip = CharNextW (wszStrip);
427 | }
428 |
429 | wchar_t* wszDataEnd =
430 | &wszData [0];
431 |
432 | if (strip_cr)
433 | {
434 | wchar_t* wszDataNext =
435 | &wszData [0];
436 |
437 | for (int i = 0; i < len; i++)
438 | {
439 | if (*wszDataNext != L'\r')
440 | {
441 | *wszDataEnd = *wszDataNext;
442 | wszDataEnd = CharNextW (wszDataEnd);
443 | }
444 |
445 | wszDataNext = CharNextW (wszDataNext);
446 | }
447 |
448 | const wchar_t* wszNext =
449 | CharNextW (wszDataNext);
450 |
451 | memset ( wszDataEnd,
452 | 0x00,
453 | reinterpret_cast (wszNext) -
454 | reinterpret_cast (wszDataEnd) );
455 |
456 | len =
457 | lstrlenW (wszData);
458 | }
459 |
460 | else
461 | {
462 | for (int i = 0; i < len; i++)
463 | {
464 | wszDataEnd = CharNextW (wszDataEnd);
465 | }
466 | }
467 |
468 | wchar_t* wszSecondToLast =
469 | CharPrevW (wszData, wszDataEnd);
470 |
471 | wchar_t* begin = nullptr;
472 | wchar_t* end = nullptr;
473 | wchar_t* wszDataCur = &wszData [0];
474 |
475 | for (wchar_t* i = wszDataCur; i < wszDataEnd && i != nullptr; i = CharNextW (i))
476 | {
477 | if (*i == L'[' && (i == wszData || *CharPrevW (&wszData [0], i) == L'\n'))
478 | {
479 | begin = CharNextW (i);
480 | }
481 |
482 | if (*i == L']' && (i == wszSecondToLast || *CharNextW (i) == L'\n'))
483 | end = i;
484 |
485 | if (begin != nullptr && end != nullptr && begin < end)
486 | {
487 | auto* sec_name = new wchar_t [end - begin + 2] { };
488 | size_t sec_len = wcrlen (begin, end);
489 | wcsncpy (sec_name, begin, sec_len);
490 |
491 | wchar_t* start = CharNextW (CharNextW (end));
492 | wchar_t* finish = start;
493 |
494 | bool eof = false;
495 | for (wchar_t* j = start; j <= wszDataEnd; j = CharNextW (j))
496 | {
497 | if (j == wszDataEnd)
498 | {
499 | finish = j;
500 | eof = true;
501 | break;
502 | }
503 |
504 | wchar_t *wszPrev = nullptr;
505 |
506 | if (*j == L'[' && (*(wszPrev = CharPrevW (start, j)) == L'\n'))
507 | {
508 | finish = wszPrev;
509 | break;
510 | }
511 | }
512 |
513 | iSK_INISection section =
514 | Process_Section (sec_name, start, finish);
515 |
516 | sections.emplace (
517 | std::make_pair (sec_name, section)
518 | );
519 |
520 | ordered_sections.emplace_back (sec_name);
521 |
522 | delete [] sec_name;
523 |
524 | if (eof)
525 | break;
526 |
527 | i = finish;
528 |
529 | end = nullptr;
530 | begin = nullptr;
531 | }
532 | }
533 | }
534 | }
535 |
536 | void
537 | __stdcall
538 | iSK_INI::import (const wchar_t* import_data)
539 | {
540 | wchar_t* wszImport = _wcsdup (import_data);
541 |
542 | if (wszImport != nullptr)
543 | {
544 | int len = lstrlenW (wszImport);
545 |
546 | // We don't want CrLf, just Lf
547 | bool strip_cr = false;
548 |
549 | wchar_t* wszStrip =
550 | &wszImport [0];
551 |
552 | // Find if the file has any Cr's
553 | for (int i = 0; i < len; i++)
554 | {
555 | if (*wszStrip == L'\r')
556 | {
557 | strip_cr = true;
558 | break;
559 | }
560 |
561 | wszStrip = CharNextW (wszStrip);
562 | }
563 |
564 | wchar_t* wszImportEnd =
565 | &wszImport [0];
566 |
567 | if (strip_cr)
568 | {
569 | wchar_t* wszImportNext =
570 | &wszImport [0];
571 |
572 | for (int i = 0; i < len; i++)
573 | {
574 | if (*wszImportNext != L'\r')
575 | {
576 | *wszImportEnd = *wszImportNext;
577 | wszImportEnd = CharNextW (wszImportEnd);
578 | }
579 |
580 | wszImportNext = CharNextW (wszImportNext);
581 | }
582 |
583 | const wchar_t* wszNext =
584 | CharNextW (wszImportNext);
585 |
586 | memset ( wszImportEnd,
587 | 0x00,
588 | reinterpret_cast (wszNext) -
589 | reinterpret_cast (wszImportEnd) );
590 |
591 | len =
592 | lstrlenW (wszImport);
593 | }
594 |
595 | else
596 | {
597 | for (int i = 0; i < (len - 1); i++)
598 | {
599 | wszImportEnd = CharNextW (wszImportEnd);
600 | }
601 | }
602 |
603 | wchar_t* wszSecondToLast =
604 | CharPrevW (wszImport, wszImportEnd);
605 |
606 | wchar_t* begin = nullptr;
607 | wchar_t* end = nullptr;
608 |
609 | wchar_t* wszImportCur = &wszImport [0];
610 |
611 | for (wchar_t* i = wszImportCur; i < wszImportEnd && i != nullptr; i = CharNextW (i))
612 | {
613 | if (*i == L'[' && (i == wszImport || *CharPrevW (&wszImport [0], i) == L'\n'))
614 | {
615 | begin = CharNextW (i);
616 | }
617 |
618 | if (*i == L']' && (i == wszSecondToLast || *CharNextW (i) == L'\n'))
619 | end = i;
620 |
621 | if (begin != nullptr && end != nullptr)
622 | {
623 | auto* sec_name = new wchar_t [end - begin + 2] { };
624 | size_t sec_len = wcrlen (begin, end);
625 | wcsncpy (sec_name, begin, sec_len);
626 |
627 | //MessageBoxW (NULL, sec_name, L"Section", MB_OK);
628 |
629 | wchar_t* start = CharNextW (CharNextW (end));
630 | wchar_t* finish = start;
631 | bool eof = false;
632 |
633 | for (wchar_t* j = start; j <= wszImportEnd; j = CharNextW (j))
634 | {
635 | if (j == wszImportEnd)
636 | {
637 | finish = j;
638 | eof = true;
639 | break;
640 | }
641 |
642 | wchar_t *wszPrev = nullptr;
643 |
644 | if (*j == L'[' && (*(wszPrev = CharPrevW (start, j)) == L'\n'))
645 | {
646 | finish = wszPrev;
647 | break;
648 | }
649 | }
650 |
651 | // Import if the section already exists
652 | if (contains_section (sec_name))
653 | {
654 | iSK_INISection& section =
655 | get_section (sec_name);
656 |
657 | Import_Section (section, start, finish);
658 | }
659 |
660 | // Insert otherwise
661 | else
662 | {
663 | iSK_INISection section =
664 | Process_Section (sec_name, start, finish);
665 |
666 | sections.emplace (
667 | std::make_pair (sec_name, section)
668 | );
669 |
670 | ordered_sections.emplace_back (sec_name);
671 | }
672 |
673 | delete [] sec_name;
674 |
675 | if (eof)
676 | break;
677 |
678 | i = finish;
679 |
680 | end = nullptr;
681 | begin = nullptr;
682 | }
683 | }
684 | }
685 |
686 | delete [] wszImport;
687 | }
688 |
689 | std::wstring invalid = L"Invalid";
690 |
691 | std::wstring&
692 | __stdcall
693 | iSK_INISection::get_value (const wchar_t* key)
694 | {
695 | auto it_key =
696 | keys.find (key);
697 |
698 | if (it_key != keys.end ())
699 | return (*it_key).second;
700 |
701 | return invalid;
702 | }
703 |
704 | void
705 | __stdcall
706 | iSK_INISection::set_name (const wchar_t* name_)
707 | {
708 | name = name_;
709 | }
710 |
711 | bool
712 | __stdcall
713 | iSK_INISection::contains_key (const wchar_t* key)
714 | {
715 | return keys.count (key) > 0;
716 | }
717 |
718 | void
719 | __stdcall
720 | iSK_INISection::add_key_value (const wchar_t* key, const wchar_t* value)
721 | {
722 | keys.emplace ( std::make_pair (key, value) );
723 | ordered_keys.emplace_back ( key );
724 | }
725 |
726 | bool
727 | __stdcall
728 | iSK_INI::contains_section (const wchar_t* section)
729 | {
730 | return sections.count (section) > 0;
731 | }
732 |
733 | iSK_INISection&
734 | __stdcall
735 | iSK_INI::get_section (const wchar_t* section)
736 | {
737 | if (! sections.count (section))
738 | ordered_sections.emplace_back (section);
739 |
740 | iSK_INISection& ret =
741 | sections [section];
742 |
743 | ret.name = section;
744 |
745 | return ret;
746 | }
747 |
748 | #include
749 |
750 | iSK_INISection&
751 | __stdcall
752 | iSK_INI::get_section_f ( _In_z_ _Printf_format_string_
753 | wchar_t const* const _Format,
754 | ... )
755 | {
756 | wchar_t wszFormatted [128] = { };
757 |
758 | int len = 0;
759 |
760 | va_list _ArgList;
761 | va_start (_ArgList, _Format);
762 | {
763 | // ASSERT: Length <= 127 characters
764 | len += vswprintf (wszFormatted, _Format, _ArgList);
765 | }
766 | va_end (_ArgList);
767 |
768 | if (! sections.count (wszFormatted))
769 | ordered_sections.emplace_back (wszFormatted);
770 |
771 | iSK_INISection& ret =
772 | sections [wszFormatted];
773 |
774 | ret.name = wszFormatted;
775 |
776 | return ret;
777 | }
778 |
779 |
780 | void
781 | __stdcall
782 | iSK_INI::write (const wchar_t* fname)
783 | {
784 | SK_CreateDirectories (fname);
785 |
786 | FILE* fOut = nullptr;
787 | errno_t ret = 0;
788 |
789 | switch (encoding_)
790 | {
791 | case INI_UTF8:
792 | TRY_FILE_IO (_wfsopen (fname, L"wtc,ccs=UTF-8", _SH_DENYNO), fname, fOut);
793 | break;
794 |
795 | // Cannot preserve this, consider adding a byte-swap on file close
796 | case INI_UTF16BE:
797 | TRY_FILE_IO (_wfsopen (fname, L"wtc,ccs=UTF-16LE", _SH_DENYNO), fname, fOut);
798 | break;
799 |
800 | default:
801 | case INI_UTF16LE:
802 | TRY_FILE_IO (_wfsopen (fname, L"wtc,ccs=UTF-16LE", _SH_DENYNO), fname, fOut);
803 | break;
804 | }
805 |
806 | if (ret != 0 || fOut == nullptr)
807 | {
808 | //SK_MessageBox (L"ERROR: Cannot open INI file for writing. Is it read-only?", fname, MB_OK | MB_ICONSTOP);
809 | return;
810 | }
811 |
812 |
813 |
814 | // Strip Empty Sections
815 | // --------------------
816 | // *** These would cause blank lines to be appended to the end of the INI file
817 | // if we did not do something about them here and now. ***
818 | //
819 | for ( auto& it : ordered_sections )
820 | {
821 | iSK_INISection& section =
822 | get_section (it.c_str ());
823 |
824 | if (section.ordered_keys.empty ())
825 | {
826 | remove_section (section.name.c_str ());
827 | }
828 | }
829 |
830 |
831 | std::wstring outbuf = L"";
832 |
833 |
834 | for ( auto& it : ordered_sections )
835 | {
836 | iSK_INISection& section =
837 | get_section (it.c_str ());
838 |
839 | if ( section.name.length () &&
840 | (! section.ordered_keys.empty ()) )
841 | {
842 | outbuf += L"[";
843 | outbuf += section.name + L"]\n";
844 |
845 | for ( auto& key_it : section.ordered_keys )
846 | {
847 | const std::wstring& val =
848 | section.get_value (key_it.c_str ());
849 |
850 | outbuf += key_it + L"=";
851 | outbuf += val + L"\n";
852 | }
853 |
854 | outbuf += L"\n";
855 | }
856 | }
857 |
858 | if (outbuf.back () == L'\n')
859 | {
860 | // Strip the unnecessary extra newline
861 | outbuf.resize (outbuf.size () - 1);
862 | }
863 |
864 | fputws (outbuf.c_str (), fOut);
865 | fclose (fOut);
866 | }
867 |
868 |
869 | iSK_INI::_TSectionMap&
870 | __stdcall
871 | iSK_INI::get_sections (void)
872 | {
873 | return sections;
874 | }
875 |
876 |
877 | HRESULT
878 | __stdcall
879 | iSK_INI::QueryInterface (THIS_ REFIID riid, void** ppvObj)
880 | {
881 | if (IsEqualGUID (riid, IID_SK_INI))
882 | {
883 | AddRef ();
884 |
885 | *ppvObj = this;
886 |
887 | return S_OK;
888 | }
889 |
890 | return E_NOTIMPL;
891 | }
892 |
893 | ULONG
894 | __stdcall
895 | iSK_INI::AddRef (THIS)
896 | {
897 | return InterlockedIncrement (&refs);
898 | }
899 |
900 | ULONG
901 | __stdcall
902 | iSK_INI::Release (THIS)
903 | {
904 | return InterlockedDecrement (&refs);
905 | }
906 |
907 | bool
908 | __stdcall
909 | iSK_INI::remove_section (const wchar_t* wszSection)
910 | {
911 | std::wstring section_w (wszSection);
912 |
913 | auto it =
914 | std::find ( ordered_sections.begin (),
915 | ordered_sections.end (),
916 | section_w );
917 |
918 | if (it != ordered_sections.end ())
919 | {
920 | ordered_sections.erase (it);
921 | sections.erase (section_w);
922 |
923 | return true;
924 | }
925 |
926 | return false;
927 | }
928 |
929 | bool
930 | __stdcall
931 | iSK_INISection::remove_key (const wchar_t* wszKey)
932 | {
933 | std::wstring key_w (wszKey);
934 |
935 | auto it =
936 | std::find ( ordered_keys.begin (),
937 | ordered_keys.end (),
938 | key_w );
939 |
940 | if (it != ordered_keys.end ())
941 | {
942 | ordered_keys.erase (it);
943 | keys.erase (key_w);
944 |
945 | return true;
946 | }
947 |
948 | return false;
949 | }
950 |
951 |
952 | HRESULT
953 | __stdcall
954 | iSK_INISection::QueryInterface (THIS_ REFIID riid, void** ppvObj)
955 | {
956 | if (IsEqualGUID (riid, IID_SK_INISection))
957 | {
958 | AddRef ();
959 |
960 | *ppvObj = this;
961 |
962 | return S_OK;
963 | }
964 |
965 | return E_NOTIMPL;
966 | }
967 |
968 | ULONG
969 | __stdcall
970 | iSK_INISection::AddRef (THIS)
971 | {
972 | return InterlockedIncrement (&refs);
973 | }
974 |
975 | ULONG
976 | __stdcall
977 | iSK_INISection::Release (THIS)
978 | {
979 | ULONG ret = InterlockedDecrement (&refs);
980 |
981 | if (ret == 0)
982 | {
983 | // Add to ToFree list in future
984 | //delete this;
985 | }
986 |
987 | return ret;
988 | }
989 |
990 |
991 | const wchar_t*
992 | iSK_INI::get_filename (void) const
993 | {
994 | return wszName;
995 | }
996 |
997 | bool
998 | iSK_INI::import_file (const wchar_t* fname)
999 | {
1000 | // We skip a few bytes (Unicode BOM) in certain circumstances, so this is the
1001 | // actual pointer we need to free...
1002 | wchar_t *alloc = nullptr;
1003 | wchar_t *wszImportName =
1004 | new wchar_t [wcslen (fname) + 2] { };
1005 |
1006 | FILE *fImportINI = nullptr;
1007 |
1008 | wcscpy (wszImportName, fname);
1009 |
1010 | TRY_FILE_IO (_wfsopen (fname, L"rbS", _SH_DENYNO), fname, fImportINI);
1011 |
1012 | if (fImportINI != nullptr)
1013 | {
1014 | auto size =
1015 | static_cast (SK_GetFileSize (fname));
1016 |
1017 | wchar_t *wszImportData = new wchar_t [size + 2] { };
1018 | alloc = wszImportData;
1019 |
1020 | fread (wszImportData, size, 1, fImportINI);
1021 |
1022 | fflush (fImportINI);
1023 | fclose (fImportINI);
1024 |
1025 | // First, consider Unicode
1026 | // UTF16-LE (All is well in the world)
1027 | if (*wszImportData == 0xFEFF)
1028 | {
1029 | ++wszImportData; // Skip the BOM
1030 | }
1031 |
1032 | // UTF16-BE (Somehow we are swapped)
1033 | else if (*wszImportData == 0xFFFE)
1034 | {
1035 | //dll_log.Log ( L"[INI Parser] Encountered Byte-Swapped Unicode INI "
1036 | // L"file ('%s'), attempting to recover...",
1037 | // wszImportName );
1038 |
1039 | wchar_t* wszSwapMe = wszImportData;
1040 |
1041 | for (int i = 0; i < size; i += 2)
1042 | {
1043 | *wszSwapMe++ =
1044 | _byteswap_ushort (*wszSwapMe);
1045 | }
1046 |
1047 | ++wszImportData; // Skip the BOM
1048 | }
1049 |
1050 | // Something else, if it's ANSI or UTF-8, let's hope Windows can figure
1051 | // out what to do...
1052 | else
1053 | {
1054 | // Skip the silly UTF8 BOM if it is present
1055 | bool utf8 = (reinterpret_cast (wszImportData)) [0] == 0xEF &&
1056 | (reinterpret_cast (wszImportData)) [1] == 0xBB &&
1057 | (reinterpret_cast (wszImportData)) [2] == 0xBF;
1058 |
1059 | const uintptr_t offset =
1060 | utf8 ? 3 : 0;
1061 |
1062 | const int real_size =
1063 | size - static_cast (offset);
1064 |
1065 | char* start_addr =
1066 | (reinterpret_cast (wszImportData)) + offset;
1067 |
1068 | auto* string =
1069 | new char [real_size];
1070 |
1071 | memcpy (string, start_addr, real_size);
1072 |
1073 | delete [] wszImportData;
1074 |
1075 | int converted_size =
1076 | MultiByteToWideChar ( CP_UTF8, 0, string, real_size, nullptr, 0 );
1077 |
1078 | if (! converted_size)
1079 | {
1080 | //dll_log.Log ( L"[INI Parser] Could not convert UTF-8 / ANSI Encoded "
1081 | // L".ini file ('%s') to UTF-16, aborting!",
1082 | // wszImportName );
1083 | wszImportData = nullptr;
1084 |
1085 | delete [] string;
1086 |
1087 | return false;
1088 | }
1089 |
1090 | wszImportData =
1091 | new wchar_t [converted_size + 1] { };
1092 |
1093 | MultiByteToWideChar ( CP_UTF8, 0, string, real_size, wszImportData, converted_size );
1094 |
1095 | //dll_log.Log ( L"[INI Parser] Converted UTF-8 INI File: '%s'",
1096 | //wszImportName );
1097 |
1098 | delete [] string;
1099 |
1100 | // No Byte-Order Marker
1101 | alloc = wszImportData;
1102 | }
1103 |
1104 | import (wszImportData);
1105 |
1106 | delete [] alloc;
1107 | wszImportData = nullptr;
1108 |
1109 | return true;
1110 | }
1111 |
1112 | return false;
1113 | }
1114 |
1115 | iSK_INI*
1116 | __stdcall
1117 | SK_CreateINI (const wchar_t* const wszName)
1118 | {
1119 | auto* pINI =
1120 | new iSK_INI (wszName);
1121 |
1122 | return pINI;
1123 | }
--------------------------------------------------------------------------------
/src/ini.h:
--------------------------------------------------------------------------------
1 | /**
2 | * This file is part of Special K.
3 | *
4 | * Special K is free software : you can redistribute it
5 | * and/or modify it under the terms of the GNU General Public License
6 | * as published by The Free Software Foundation, either version 3 of
7 | * the License, or (at your option) any later version.
8 | *
9 | * Special K is distributed in the hope that it will be useful,
10 | *
11 | * But WITHOUT ANY WARRANTY; without even the implied warranty of
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 | * GNU General Public License for more details.
14 | *
15 | * You should have received a copy of the GNU General Public License
16 | * along with Special K.
17 | *
18 | * If not, see .
19 | *
20 | **/
21 | #ifndef __SK__INI_H__
22 | #define __SK__INI_H__
23 |
24 | #include
25 | #include
26 | #include
27 |
28 | #include
29 |
30 | // {B526D074-2F4D-4BAE-B6EC-11CB3779B199}
31 | static const GUID IID_SK_INISection =
32 | { 0xb526d074, 0x2f4d, 0x4bae, { 0xb6, 0xec, 0x11, 0xcb, 0x37, 0x79, 0xb1, 0x99 } };
33 |
34 | interface iSK_INISection : public IUnknown
35 | {
36 | public:
37 | iSK_INISection (void) = default;
38 |
39 | iSK_INISection (const wchar_t* section_name) {
40 | name = section_name;
41 | }
42 |
43 | /*** IUnknown methods ***/
44 | STDMETHOD ( QueryInterface)(THIS_ REFIID riid, void** ppvObj);
45 | STDMETHOD_ (ULONG, AddRef) (THIS);
46 | STDMETHOD_ (ULONG, Release) (THIS);
47 |
48 | STDMETHOD_ (std::wstring&, get_value) (const wchar_t* key);
49 | STDMETHOD_ (void, set_name) (const wchar_t* name_);
50 | STDMETHOD_ (bool, contains_key) (const wchar_t* key);
51 | STDMETHOD_ (void, add_key_value)(const wchar_t* key, const wchar_t* value);
52 | STDMETHOD_ (bool, remove_key) (const wchar_t* key);
53 |
54 | //protected:
55 | //private:
56 | std::wstring name;
57 | std::unordered_map keys;
58 | std::vector ordered_keys;
59 |
60 | ULONG refs = 0;
61 | };
62 |
63 | // {DD2B1E00-6C14-4659-8B45-FCEF1BC2C724}
64 | static const GUID IID_SK_INI =
65 | { 0xdd2b1e00, 0x6c14, 0x4659, { 0x8b, 0x45, 0xfc, 0xef, 0x1b, 0xc2, 0xc7, 0x24 } };
66 |
67 | interface iSK_INI : public IUnknown
68 | {
69 | using _TSectionMap =
70 | const std::unordered_map ;
71 |
72 | iSK_INI (const wchar_t* filename);
73 | ~iSK_INI (void);
74 |
75 | /*** IUnknown methods ***/
76 | STDMETHOD ( QueryInterface)(THIS_ REFIID riid, void** ppvObj);
77 | STDMETHOD_ (ULONG, AddRef) (THIS);
78 | STDMETHOD_ (ULONG, Release) (THIS);
79 |
80 | STDMETHOD_ (void, parse) (THIS);
81 | STDMETHOD_ (void, import) (THIS_ const wchar_t* import_data);
82 | STDMETHOD_ (void, write) (THIS_ const wchar_t* fname);
83 |
84 | STDMETHOD_ (_TSectionMap&, get_sections) (THIS);
85 | STDMETHOD_ (iSK_INISection&, get_section) (const wchar_t* section);
86 | STDMETHOD_ (bool, contains_section)(const wchar_t* section);
87 | STDMETHOD_ (bool, remove_section) (const wchar_t* section);
88 |
89 | STDMETHOD_ (iSK_INISection&, get_section_f) ( THIS_ _In_z_ _Printf_format_string_
90 | wchar_t const* const _Format,
91 | ... );
92 | STDMETHOD_ (const wchar_t*, get_filename) (THIS) const;
93 | STDMETHOD_ (bool, import_file) (const wchar_t* fname);
94 |
95 | protected:
96 | private:
97 | FILE* fINI = nullptr;
98 |
99 | wchar_t* wszName = nullptr;
100 | wchar_t* wszData = nullptr;
101 |
102 | std::unordered_map <
103 | std::wstring, iSK_INISection
104 | > sections;
105 |
106 | // Preserve insertion order so that we write the INI file in the
107 | // same order we read it. Otherwise things get jumbled around
108 | // arbitrarily as the map is re-hashed.
109 | std::vector
110 | ordered_sections;
111 |
112 | // Preserve File Encoding
113 | enum CharacterEncoding {
114 | INI_INVALID = 0x00,
115 | INI_UTF8 = 0x01,
116 | INI_UTF16LE = 0x02,
117 | INI_UTF16BE = 0x04 // Not natively supported, but can be converted
118 | } encoding_;
119 |
120 | ULONG refs = 0;
121 | };
122 |
123 | #endif /* __SK__INI_H__ */
--------------------------------------------------------------------------------
/src/injection.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 |
3 | #include "resource.h"
4 |
5 | #include
6 | #include
7 | #include
8 |
9 | HMODULE hModGlobal = nullptr;
10 |
11 | #include "injection.h"
12 | #include "system_tray.h"
13 | #include "SKIM.h"
14 |
15 | SKX_RemoveCBTHook_pfn SKX_RemoveCBTHook = nullptr;
16 | SKX_InstallCBTHook_pfn SKX_InstallCBTHook = nullptr;
17 | SKX_IsHookingCBT_pfn SKX_IsHookingCBT = nullptr;
18 | SKX_GetInjectedPIDs_pfn SKX_GetInjectedPIDs = nullptr;
19 |
20 | HMODULE
21 | SKIM_GlobalInject_Load (void)
22 | {
23 | hModGlobal = GetModuleHandleW (L"SpecialK64.dll");
24 |
25 | if (hModGlobal == nullptr)
26 | hModGlobal = LoadLibraryW (L"SpecialK64.dll");
27 |
28 | if (hModGlobal != nullptr)
29 | {
30 | SKX_RemoveCBTHook = (SKX_RemoveCBTHook_pfn)
31 | GetProcAddress (hModGlobal, "SKX_RemoveCBTHook");
32 |
33 | SKX_InstallCBTHook = (SKX_InstallCBTHook_pfn)
34 | GetProcAddress (hModGlobal, "SKX_InstallCBTHook");
35 |
36 | SKX_IsHookingCBT = (SKX_IsHookingCBT_pfn)
37 | GetProcAddress (hModGlobal, "SKX_IsHookingCBT");
38 |
39 | SKX_GetInjectedPIDs = (SKX_GetInjectedPIDs_pfn)
40 | GetProcAddress (hModGlobal, "SKX_GetInjectedPIDs");
41 | }
42 |
43 | return hModGlobal;
44 | }
45 |
46 | BOOL
47 | SKIM_GlobalInject_Free (void)
48 | {
49 | if (hModGlobal != nullptr)
50 | {
51 | if (FreeLibrary (hModGlobal))
52 | hModGlobal = nullptr;
53 | }
54 |
55 | if (hModGlobal == nullptr)
56 | {
57 | SKX_RemoveCBTHook = nullptr;
58 | SKX_InstallCBTHook = nullptr;
59 | SKX_IsHookingCBT = nullptr;
60 | SKX_GetInjectedPIDs = nullptr;
61 | }
62 |
63 | if (hModGlobal == nullptr)
64 | return TRUE;
65 |
66 | //while (! FreeLibrary (hModGlobal))
67 | // ;
68 |
69 | hModGlobal = nullptr;
70 |
71 | return TRUE;
72 | }
73 |
74 | bool
75 | SKIM_GlobalInject_Start (void)
76 | {
77 | CoInitializeEx (nullptr, COINIT_MULTITHREADED);
78 |
79 | if (SKIM_GlobalInject_Load ())
80 | {
81 | wchar_t wszInjectionCacheLock [MAX_PATH + 2] = { };
82 |
83 | uint32_t dwStrLen = MAX_PATH;
84 | SKIM_Util_GetDocumentsDir (wszInjectionCacheLock, &dwStrLen);
85 |
86 | PathAppendW (wszInjectionCacheLock, L"My Mods\\SpecialK\\Global\\injection.ini.lock");
87 | DeleteFileW (wszInjectionCacheLock);
88 |
89 | if (GetFileAttributes (L"SpecialK32.dll") != INVALID_FILE_ATTRIBUTES)
90 | {
91 | // Wait / clear the 32-bit injector's PID file (it might be leftover from an unclean reboot)
92 | //
93 | if (GetFileAttributes (L"SpecialK32.pid") != INVALID_FILE_ATTRIBUTES)
94 | SKIM_GlobalInject_Stop (false);
95 |
96 | if (GetFileAttributes (L"SpecialK32.pid") == INVALID_FILE_ATTRIBUTES)
97 | {
98 | wchar_t wszPath [MAX_PATH * 2] = { };
99 | wchar_t wszFullPath [MAX_PATH * 2] = { };
100 |
101 | GetCurrentDirectoryW (MAX_PATH * 2 - 1, wszPath);
102 | GetSystemWow64DirectoryW (wszFullPath, MAX_PATH * 2 -1);
103 |
104 | PathAppendW (wszFullPath, L"rundll32.exe");
105 | ShellExecuteW (nullptr, L"open", wszFullPath,
106 | L"SpecialK32.dll,RunDLL_InjectionManager Install", wszPath, SW_HIDE);
107 |
108 | while (GetFileAttributes (L"SpecialK32.pid") == INVALID_FILE_ATTRIBUTES)
109 | {
110 | Sleep (133UL);
111 | }
112 | }
113 | }
114 |
115 | if (! SKX_IsHookingCBT ())
116 | {
117 | SKX_InstallCBTHook ();
118 |
119 | if (SKX_IsHookingCBT ())
120 | return true;
121 | }
122 | }
123 |
124 | return false;
125 | }
126 |
127 | #include
128 | #include
129 | #include
130 |
131 | bool
132 | SKIM_GetProgramsDir (wchar_t* buf, uint32_t* pdwLen)
133 | {
134 | CComHeapPtr str;
135 | CHandle hToken;
136 |
137 | if (! OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &hToken.m_h))
138 | return false;
139 |
140 | if ( SUCCEEDED (
141 | SHGetKnownFolderPath (
142 | FOLDERID_Programs, 0, hToken, &str
143 | )
144 | )
145 | )
146 | {
147 | if (buf != nullptr && pdwLen != nullptr && *pdwLen > 0) {
148 | wcsncpy (buf, str, *pdwLen);
149 | }
150 |
151 | return true;
152 | }
153 |
154 | return false;
155 | }
156 |
157 | bool
158 | SKIM_GetStartupDir (wchar_t* buf, uint32_t* pdwLen)
159 | {
160 | CComHeapPtr str;
161 | CHandle hToken;
162 |
163 | if (! OpenProcessToken (GetCurrentProcess (), TOKEN_READ, &hToken.m_h))
164 | return false;
165 |
166 | if ( SUCCEEDED (
167 | SHGetKnownFolderPath (
168 | FOLDERID_Startup, 0, hToken, &str
169 | )
170 | )
171 | )
172 | {
173 | if (buf != nullptr && pdwLen != nullptr && *pdwLen > 0) {
174 | wcsncpy (buf, str, *pdwLen);
175 | }
176 |
177 | return true;
178 | }
179 |
180 | return false;
181 | }
182 |
183 | std::wstring
184 | SKIM_GetStartupShortcut (void)
185 | {
186 | wchar_t wszLink [MAX_PATH * 2] = { };
187 | DWORD dwLen = MAX_PATH * 2 - 1;
188 |
189 | SKIM_GetStartupDir (wszLink, (uint32_t *)&dwLen);
190 |
191 | PathAppend (wszLink, L"SKIM64.lnk");
192 |
193 | return wszLink;
194 | }
195 |
196 | std::wstring
197 | SKIM_GetStartMenuShortcut (void)
198 | {
199 | wchar_t wszLink [MAX_PATH * 2] = { };
200 | DWORD dwLen = MAX_PATH * 2 - 1;
201 |
202 | SKIM_GetProgramsDir (wszLink, (uint32_t *)&dwLen);
203 |
204 | PathAppend (wszLink, L"Special K\\SKIM64.lnk");
205 |
206 | return wszLink;
207 | }
208 |
209 | bool
210 | SKIM_IsLaunchedAtStartup (void)
211 | {
212 | std::wstring link_file = SKIM_GetStartupShortcut ();
213 |
214 | HRESULT hr = E_FAIL;
215 | CComPtr psl = nullptr;
216 |
217 | hr = CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
218 |
219 | if (SUCCEEDED (hr))
220 | {
221 | CComPtr ppf = nullptr;
222 |
223 | hr = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
224 |
225 | if (SUCCEEDED (hr))
226 | {
227 | hr = ppf->Load (link_file.c_str (), STGM_READ);
228 |
229 | if (SUCCEEDED (hr))
230 | {
231 | return true;
232 | }
233 | }
234 | }
235 |
236 | return false;
237 | }
238 |
239 | bool
240 | SKIM_SetStartupInjection (bool enable, wchar_t* wszExecutable)
241 | {
242 | if (enable && (! SKIM_IsLaunchedAtStartup ()))
243 | {
244 | std::wstring link_file = SKIM_GetStartupShortcut ();
245 |
246 | HRESULT hr = E_FAIL;
247 | CComPtr psl = nullptr;
248 |
249 | hr = CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
250 |
251 | if (SUCCEEDED (hr))
252 | {
253 | std::unique_ptr work_dir (_wcsdup (wszExecutable));
254 | PathRemoveFileSpecW (work_dir.get ());
255 |
256 | psl->SetShowCmd (SW_NORMAL);
257 | psl->SetPath (wszExecutable);
258 | psl->SetWorkingDirectory (work_dir.get ());
259 | psl->SetDescription (L"Start Special K Injection with Windows");
260 | psl->SetArguments (L"+Inject");
261 | psl->SetIconLocation (wszExecutable, 1);
262 |
263 | CComPtr ppf = nullptr;
264 |
265 | hr = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
266 |
267 | if (SUCCEEDED (hr))
268 | {
269 | hr = ppf->Save (link_file.c_str (), TRUE);
270 |
271 | if (SUCCEEDED (hr))
272 | {
273 | SKIM_Tray_UpdateStartup ();
274 | return true;
275 | }
276 | }
277 | }
278 |
279 | return false;
280 | }
281 |
282 | else if ((! enable) && SKIM_IsLaunchedAtStartup ())
283 | {
284 | bool ret = DeleteFileW (SKIM_GetStartupShortcut ().c_str ());
285 |
286 | SKIM_Tray_UpdateStartup ();
287 |
288 | return ret;
289 | }
290 |
291 | else
292 | {
293 | SKIM_Tray_UpdateStartup ();
294 | return true;
295 | }
296 | }
297 |
298 | bool
299 | SKIM_SetStartMenuLink (bool enable, wchar_t* wszExecutable)
300 | {
301 | if (enable)
302 | {
303 | std::wstring link_file = SKIM_GetStartMenuShortcut ();
304 |
305 | SKIM_Util_CreateDirectories (link_file.c_str ());
306 |
307 | HRESULT hr = E_FAIL;
308 | CComPtr psl = nullptr;
309 |
310 | hr = CoCreateInstance (CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID *)&psl);
311 |
312 | if (SUCCEEDED (hr))
313 | {
314 | std::unique_ptr work_dir (_wcsdup (wszExecutable));
315 | PathRemoveFileSpecW (work_dir.get ());
316 |
317 | psl->SetShowCmd (SW_NORMAL);
318 | psl->SetPath (wszExecutable);
319 | psl->SetWorkingDirectory (work_dir.get ());
320 | psl->SetDescription (L"Special K Install Manager");
321 | psl->SetArguments (L"");
322 | psl->SetIconLocation (wszExecutable, 0);
323 |
324 | CComPtr ppf = nullptr;
325 |
326 | hr = psl->QueryInterface (IID_IPersistFile, (void **)&ppf);
327 |
328 | if (SUCCEEDED (hr))
329 | {
330 | hr = ppf->Save (link_file.c_str (), TRUE);
331 |
332 | if (SUCCEEDED (hr))
333 | {
334 | return true;
335 | }
336 | }
337 | }
338 |
339 | return false;
340 | }
341 |
342 | else if ((! enable))
343 | {
344 | bool ret =
345 | DeleteFileW (SKIM_GetStartMenuShortcut ().c_str ());
346 |
347 | if (ret)
348 | {
349 | std::unique_ptr start_menu_path (
350 | _wcsdup (SKIM_GetStartMenuShortcut ().c_str ())
351 | );
352 |
353 | PathRemoveFileSpec (start_menu_path.get ());
354 | ret = RemoveDirectoryW (start_menu_path.get ());
355 | }
356 |
357 | return ret;
358 | }
359 |
360 | return false;
361 | }
362 |
363 | bool
364 | SKIM_GlobalInject_Stop (bool confirm)
365 | {
366 | UNREFERENCED_PARAMETER (confirm);
367 |
368 | if (GetFileAttributes (L"SpecialK32.dll") != INVALID_FILE_ATTRIBUTES)
369 | {
370 | if (GetFileAttributes (L"SpecialK32.pid") != INVALID_FILE_ATTRIBUTES)
371 | {
372 | wchar_t wszPath [MAX_PATH * 2] = { };
373 | wchar_t wszFullPath [MAX_PATH * 2] = { };
374 |
375 | GetCurrentDirectoryW (MAX_PATH * 2 - 1, wszPath);
376 | GetSystemWow64DirectoryW (wszFullPath, MAX_PATH * 2 -1);
377 |
378 | PathAppendW (wszFullPath, L"rundll32.exe");
379 | ShellExecuteW (nullptr, L"open", wszFullPath, L"SpecialK32.dll,RunDLL_InjectionManager Remove", wszPath, SW_HIDE);
380 |
381 | int tries = 0;
382 |
383 | while (GetFileAttributes (L"SpecialK32.pid") != INVALID_FILE_ATTRIBUTES)
384 | {
385 | Sleep (133UL);
386 | ++tries;
387 |
388 | if (tries > 3)
389 | DeleteFileW (L"SpecialK32.pid");
390 | }
391 | }
392 | }
393 |
394 | if (SKIM_GlobalInject_Load ())
395 | {
396 | if (SKX_IsHookingCBT ())
397 | {
398 | SKX_RemoveCBTHook ();
399 |
400 | //return false;
401 | }
402 | }
403 |
404 | return true;
405 | }
406 |
407 | bool
408 | SKIM_GlobalInject_Stop (HWND hWndDlg, bool confirm)
409 | {
410 | if (SKIM_GlobalInject_Stop (confirm))
411 | {
412 | SetWindowText (GetDlgItem (hWndDlg, IDC_MANAGE_CMD), L"Start Injecting");
413 |
414 | SKIM_Tray_Stop ();
415 |
416 | return true;
417 | }
418 |
419 | return false;
420 | }
421 |
422 | bool
423 | SKIM_GlobalInject_Start (HWND hWndDlg)
424 | {
425 | if (SKIM_GlobalInject_Start ())
426 | {
427 | SetWindowText (GetDlgItem (hWndDlg, IDC_MANAGE_CMD), L"Stop Injecting");
428 |
429 | SKIM_Tray_Start ();
430 |
431 | return true;
432 | }
433 |
434 | return false;
435 | }
436 |
437 | bool
438 | SKIM_GlobalInject_StartStop (HWND hWndDlg, bool confirm)
439 | {
440 | if (SKIM_GetInjectorState ())
441 | {
442 | return SKIM_GlobalInject_Stop (hWndDlg, confirm);
443 | }
444 |
445 | else
446 | {
447 | return SKIM_GlobalInject_Start (hWndDlg);
448 | }
449 | }
450 |
451 | size_t
452 | SKIM_SummarizeInjectedPIDs (std::wstring& out)
453 | {
454 | size_t count = SKX_GetInjectedPIDs ?
455 | SKX_GetInjectedPIDs (nullptr, 0) : 0;
456 |
457 | DWORD dwPIDs [128] = { };
458 | wchar_t wszFileName [MAX_PATH] = { };
459 |
460 | if (SKX_GetInjectedPIDs)
461 | {
462 | SKX_GetInjectedPIDs (dwPIDs, count + 1);
463 |
464 | for (size_t i = 0; i < count; i++)
465 | {
466 | CHandle hProc (
467 | OpenProcess ( PROCESS_QUERY_INFORMATION,
468 | FALSE,
469 | dwPIDs [i] )
470 | );
471 |
472 | if (hProc != nullptr)
473 | {
474 | DWORD dwLen = MAX_PATH;
475 | QueryFullProcessImageName (hProc, 0x0, wszFileName, &dwLen);
476 |
477 | PathStripPath (wszFileName);
478 |
479 | out += L"\n ";
480 | out += wszFileName;
481 | }
482 | }
483 | }
484 |
485 | return count;
486 | }
487 |
488 | void
489 | SKIM_StopInjectingAndExit (HWND hWndDlg, bool confirm)
490 | {
491 | if (SKIM_GetInjectorState ())
492 | {
493 | SKIM_GlobalInject_Stop (hWndDlg, confirm);
494 | }
495 |
496 | SKIM_Exit ();
497 | }
498 |
499 | // 0 = Removed
500 | // 1 = Installed
501 | int
502 | SKIM_GetInjectorState (void)
503 | {
504 | #ifdef _WIN64
505 | HMODULE hMod = LoadLibrary (L"SpecialK64.dll");
506 | #else
507 | HMODULE hMod = LoadLibrary (L"SpecialK32.dll");
508 | #endif
509 |
510 | using SKX_IsHookingCBT_pfn = bool (WINAPI *)(void);
511 |
512 | if (hMod != nullptr)
513 | {
514 | if (! SKX_IsHookingCBT)
515 | SKX_IsHookingCBT =
516 | (SKX_IsHookingCBT_pfn)GetProcAddress (hMod, "SKX_IsHookingCBT");
517 |
518 | int ret = 0;
519 |
520 | if (SKX_IsHookingCBT != nullptr)
521 | {
522 | ret = SKX_IsHookingCBT ( ) ? 1 : 0;
523 | }
524 |
525 | FreeLibrary (hMod);
526 |
527 | return ret;
528 | }
529 |
530 | return 0;
531 | }
--------------------------------------------------------------------------------
/src/injection.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #define _CRT_SECURE_NO_WARNINGS
4 | #define _CRT_NON_CONFORMING_WCSTOK
5 | #define _CRT_NON_CONFORMING_SWPRINTFS
6 |
7 | #pragma warning (disable: 4091)
8 |
9 | #include "stdafx.h"
10 |
11 | #include "resource.h"
12 |
13 | #include
14 |
15 | #include
16 | #include
17 | #include
18 |
19 | #include
20 | #include
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 | #include
27 |
28 | //HMODULE hModGlobal = 0;//LoadLibrary (L"SpecialK64.dll");
29 |
30 | typedef void (WINAPI *SKX_InstallCBTHook_pfn) (void);
31 | typedef void (WINAPI *SKX_RemoveCBTHook_pfn) (void);
32 | typedef bool (WINAPI *SKX_IsHookingCBT_pfn) (void);
33 | typedef size_t (WINAPI *SKX_GetInjectedPIDs_pfn) (DWORD*, size_t);
34 |
35 | //SKX_RemoveCBTHook_pfn SKX_RemoveCBTHook = nullptr;
36 | //SKX_InstallCBTHook_pfn SKX_InstallCBTHook = nullptr;
37 | //SKX_IsHookingCBT_pfn SKX_IsHookingCBT = nullptr;
38 | //SKX_GetInjectedPIDs_pfn SKX_GetInjectedPIDs = nullptr;
39 |
40 | HMODULE SKIM_GlobalInject_Load (void);
41 | BOOL SKIM_GlobalInject_Free (void);
42 | bool SKIM_GlobalInject_Start (void);
43 | std::wstring SKIM_GetStartupShortcut (void);
44 | std::wstring SKIM_GetStartMenuShortcut (void);
45 | bool SKIM_IsLaunchedAtStartup (void);
46 | bool SKIM_SetStartupInjection (bool enable, wchar_t* wszExecutable);
47 | bool SKIM_SetStartMenuLink (bool enable, wchar_t* wszExecutable);
48 | bool SKIM_GlobalInject_Stop (bool confirm = true);
49 | bool SKIM_GlobalInject_Stop (HWND hWndDlg, bool confirm = true);
50 | bool SKIM_GlobalInject_Start (HWND hWndDlg);
51 | bool SKIM_GlobalInject_StartStop (HWND hWndDlg, bool confirm = true);
52 | size_t SKIM_SummarizeInjectedPIDs (std::wstring& out);
53 | void SKIM_StopInjectingAndExit (HWND hWndDlg, bool confirm = true);
54 | int SKIM_GetInjectorState (void);
--------------------------------------------------------------------------------
/src/network.cpp:
--------------------------------------------------------------------------------
1 | #include "stdafx.h"
2 |
3 | #include
4 | #pragma comment (lib, "wininet.lib")
5 |
6 | #include "product.h"
7 | #include "SKIM.h"
8 | #include "resource.h"
9 |
10 | #include
11 |
12 | #include
13 | #pragma comment (lib, "winmm.lib")
14 |
15 | bool
16 | __stdcall
17 | SKIM_FetchDLCManagerDLL ( sk_product_t& product,
18 | const wchar_t *wszRemoteFile =
19 | #ifndef _WIN64
20 | L"installer.dll"
21 | #else
22 | L"installer64.dll"
23 | #endif
24 | )
25 | {
26 | UNREFERENCED_PARAMETER (product);
27 |
28 | DWORD dwInetCtx = 0;
29 |
30 | HINTERNET hInetRoot =
31 | InternetOpen (
32 | L"Special K Install Manager",
33 | INTERNET_OPEN_TYPE_DIRECT,
34 | nullptr, nullptr,
35 | 0x00
36 | );
37 |
38 | if (! hInetRoot)
39 | return false;
40 |
41 | HINTERNET hInetGitHub =
42 | InternetConnect ( hInetRoot,
43 | L"raw.githubusercontent.com",
44 | INTERNET_DEFAULT_HTTP_PORT,
45 | nullptr, nullptr,
46 | INTERNET_SERVICE_HTTP,
47 | 0x00,
48 | (DWORD_PTR)&dwInetCtx );
49 |
50 | if (! hInetGitHub) {
51 | InternetCloseHandle (hInetRoot);
52 | return false;
53 | }
54 |
55 | wchar_t wszRemoteRepoURL [INTERNET_MAX_PATH_LENGTH] = { };
56 |
57 | _snwprintf ( wszRemoteRepoURL,
58 | INTERNET_MAX_PATH_LENGTH,
59 | L"/Kaldaien/SpecialK/0.8.x/%s",
60 | /*product.wszRepoName,*/ wszRemoteFile );
61 |
62 | PCWSTR rgpszAcceptTypes [] = { L"*/*", nullptr };
63 |
64 | HINTERNET hInetGitHubOpen =
65 | HttpOpenRequest ( hInetGitHub,
66 | nullptr,
67 | wszRemoteRepoURL,
68 | L"HTTP/1.1",
69 | nullptr,
70 | rgpszAcceptTypes,
71 | INTERNET_FLAG_MAKE_PERSISTENT | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
72 | INTERNET_FLAG_CACHE_IF_NET_FAIL | INTERNET_FLAG_IGNORE_CERT_CN_INVALID |
73 | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_CACHE_ASYNC |
74 | INTERNET_FLAG_NEED_FILE,
75 | (DWORD_PTR)&dwInetCtx );
76 |
77 | if (! hInetGitHubOpen) {
78 | InternetCloseHandle (hInetGitHub);
79 | InternetCloseHandle (hInetRoot);
80 | return false;
81 | }
82 |
83 | if ( HttpSendRequestW ( hInetGitHubOpen,
84 | nullptr,
85 | 0,
86 | nullptr,
87 | 0 ) )
88 | {
89 | DWORD dwSize;
90 |
91 | if ( InternetQueryDataAvailable ( hInetGitHubOpen,
92 | &dwSize,
93 | 0x00, NULL )
94 | )
95 | {
96 | DWORD dwAttribs =
97 | GetFileAttributes (wszRemoteFile);
98 |
99 | if (dwAttribs == INVALID_FILE_ATTRIBUTES)
100 | dwAttribs = FILE_ATTRIBUTE_NORMAL;
101 |
102 | CHandle hVersionFile (
103 | CreateFileW ( wszRemoteFile,
104 | GENERIC_READ | GENERIC_WRITE,
105 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
106 | nullptr,
107 | CREATE_ALWAYS,
108 | dwAttribs |
109 | FILE_FLAG_SEQUENTIAL_SCAN,
110 | nullptr ) );
111 |
112 | while (hVersionFile != INVALID_HANDLE_VALUE && dwSize > 0)
113 | {
114 | DWORD dwRead = 0;
115 |
116 | CHeapPtr pData;
117 | if (! pData.Allocate (dwSize))
118 | break;
119 |
120 | if ( InternetReadFile ( hInetGitHubOpen,
121 | pData,
122 | dwSize,
123 | &dwRead )
124 | )
125 | {
126 | DWORD dwWritten = 0;
127 |
128 | WriteFile ( hVersionFile,
129 | pData,
130 | dwRead,
131 | &dwWritten,
132 | nullptr );
133 | }
134 |
135 | if (! InternetQueryDataAvailable ( hInetGitHubOpen,
136 | &dwSize,
137 | 0x00, NULL
138 | )
139 | ) break;
140 | }
141 | }
142 |
143 | HttpEndRequest ( hInetGitHubOpen, nullptr, 0x00, 0 );
144 | }
145 |
146 | InternetCloseHandle (hInetGitHubOpen);
147 | InternetCloseHandle (hInetGitHub);
148 | InternetCloseHandle (hInetRoot);
149 |
150 | return true;
151 | }
152 |
153 | struct sk_internet_get_t {
154 | wchar_t wszHostName [INTERNET_MAX_HOST_NAME_LENGTH];
155 | wchar_t wszHostPath [INTERNET_MAX_PATH_LENGTH];
156 | wchar_t wszLocalPath [MAX_PATH];
157 | wchar_t wszAppend [MAX_PATH]; // If non-zero length, then append
158 | // to this file.
159 | uint64_t size;
160 | uint32_t crc32c;
161 | int status;
162 | };
163 |
164 | struct sk_internet_head_t {
165 | wchar_t wszHostName [INTERNET_MAX_HOST_NAME_LENGTH];
166 | wchar_t wszHostPath [INTERNET_MAX_PATH_LENGTH];
167 | HANDLE hThread;
168 | uint64_t size;
169 | };
170 |
171 | enum {
172 | WM_FILE_PROGRESS = WM_APP,
173 | WM_FILE_DONE,
174 | WM_FILE_CANCELLED
175 | };
176 |
177 | enum {
178 | STATUS_FETCHED = 1,
179 | STATUS_OTHER = 2,
180 | STATUS_CANCELLED = 4,
181 | STATUS_FAILED = 8
182 | };
183 |
184 | static std::queue files_to_fetch;
185 | static std::vector files_to_lookup;
186 | static sk_internet_get_t file_to_fetch;
187 |
188 | static uint64_t total_fetch_size = 0ULL;
189 | static uint64_t total_fetched_bytes = 0ULL;
190 |
191 | static uint32_t file_fetched_size = 0UL;
192 | static uint32_t file_fetched_bytes = 0UL;
193 |
194 | static HWND hWndDownloadDialog = nullptr;
195 | static HANDLE hWorkerThread = nullptr;
196 | static HANDLE hTerminateEvent = nullptr;
197 |
198 | static HWND hWndFileProgress;
199 | static HWND hWndTotalProgress;
200 |
201 | struct {
202 | HWND hWndCtl;
203 | COLORREF op_color;
204 | wchar_t wszOp [32];
205 | wchar_t wszName [128];
206 |
207 | void set (COLORREF color, const wchar_t* op, const wchar_t* name) {
208 | if (op_color != color || wcsncmp (op, wszOp, 32) || wcsncmp (name, wszName, 128)) {
209 | op_color = color;
210 | wcsncpy (wszOp, op, 32);
211 | wcsncpy (wszName, name, 128);
212 |
213 | CHARFORMAT2W cf2;
214 | ZeroMemory (&cf2, sizeof CHARFORMAT2W);
215 |
216 | cf2.cbSize = sizeof CHARFORMAT2W;
217 |
218 | cf2.crTextColor = color;
219 | cf2.crBackColor = RGB (10, 10, 10);
220 | cf2.dwMask = CFM_BOLD | CFM_COLOR | CFM_BACKCOLOR;
221 | cf2.dwEffects = CFE_BOLD;
222 |
223 | SendMessage (hWndCtl, EM_SETSEL, 0, (LPARAM)wcslen (op));
224 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
225 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)op);
226 |
227 | size_t dwEndSel = wcslen (op);
228 |
229 | cf2.crTextColor = RGB (10, 10, 10);
230 | cf2.dwMask = CFM_BOLD | CFM_COLOR | CFM_EFFECTS;
231 | cf2.dwEffects = CFE_BOLD | CFE_AUTOBACKCOLOR;
232 |
233 | SendMessage (hWndCtl, EM_SETSEL, dwEndSel, (LPARAM)(dwEndSel + wcslen (L"\t")));
234 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
235 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)L"\t");
236 |
237 | dwEndSel += wcslen (L"\t");
238 |
239 | SendMessage (hWndCtl, EM_SETSEL, dwEndSel, (LPARAM)(dwEndSel + wcslen (name)));
240 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
241 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)name);
242 | }
243 | }
244 | } fetch_op;
245 |
246 | struct {
247 | int32_t current = 0L;
248 |
249 | void set (HWND hWndParent, int32_t new_pos) {
250 | if (current != new_pos) {
251 | current = new_pos;
252 |
253 | HWND hWndTotalProgress_ =
254 | GetDlgItem (hWndParent, IDC_DLC_TOTAL_PROGRESS);
255 |
256 | SendMessage ( hWndTotalProgress_,
257 | PBM_SETPOS,
258 | current,
259 | 0UL );
260 | }
261 | }
262 | } fetch_total_progress;
263 |
264 | struct {
265 | int32_t current = 0L;
266 |
267 | void set (HWND hWndParent, int32_t new_pos) {
268 | if (current != new_pos) {
269 | current = new_pos;
270 |
271 | HWND hWndFileProgress_ =
272 | GetDlgItem (hWndParent, IDC_DLC_FILE_PROGRESS);
273 |
274 | SendMessage ( hWndFileProgress_,
275 | PBM_SETPOS,
276 | current,
277 | 0UL );
278 | }
279 | }
280 | } fetch_file_progress;
281 |
282 | struct {
283 | HWND hWndCtl = nullptr;
284 | wchar_t wszFile [MAX_PATH + 2] = { };
285 |
286 | void set (const wchar_t* val)
287 | {
288 | if (_wcsnicmp (val, wszFile, MAX_PATH))
289 | {
290 | wcsncpy (wszFile, val, MAX_PATH);
291 |
292 | CHARFORMAT2W cf2;
293 | ZeroMemory (&cf2, sizeof CHARFORMAT2W);
294 |
295 | cf2.cbSize = sizeof CHARFORMAT2W;
296 |
297 | cf2.crTextColor = RGB (10, 10, 10);
298 | cf2.dwMask = CFM_BOLD | CFM_COLOR;
299 | cf2.dwEffects = CFE_BOLD;
300 |
301 | SendMessage (hWndCtl, EM_SETSEL, 0, -1);
302 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
303 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)wszFile);
304 | }
305 | }
306 | } fetch_current_file;
307 |
308 | struct {
309 | HWND hWndCtl = nullptr;
310 | wchar_t wszSize [66] = { };
311 |
312 | void set (const wchar_t* val)
313 | {
314 | if (_wcsnicmp (val, wszSize, 64))
315 | {
316 | wcsncpy (wszSize, val, 64);
317 |
318 | CHARFORMAT2W cf2;
319 | ZeroMemory (&cf2, sizeof CHARFORMAT2W);
320 |
321 | cf2.cbSize = sizeof CHARFORMAT2W;
322 |
323 | cf2.crTextColor = RGB (248, 22, 22);
324 | cf2.dwMask = CFM_BOLD | CFM_COLOR;
325 | cf2.dwEffects = CFE_BOLD;
326 |
327 | SendMessage (hWndCtl, EM_SETSEL, 0, -1);
328 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
329 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)val);
330 | }
331 | }
332 | } fetch_current_size;
333 |
334 | struct {
335 | HWND hWndCtl = nullptr;
336 | wchar_t wszSize [62] = { };
337 |
338 | void set (const wchar_t* val)
339 | {
340 | if (_wcsnicmp (val, wszSize, 64))
341 | {
342 | wcsncpy (wszSize, val, 64);
343 |
344 | CHARFORMAT2W cf2;
345 | ZeroMemory (&cf2, sizeof CHARFORMAT2W);
346 |
347 | cf2.cbSize = sizeof CHARFORMAT2W;
348 |
349 | cf2.crTextColor = RGB (248, 22, 22);
350 | cf2.dwMask = CFM_BOLD | CFM_COLOR;
351 | cf2.dwEffects = CFE_BOLD;
352 |
353 | SendMessage (hWndCtl, EM_SETSEL, 0, -1);
354 | SendMessage (hWndCtl, EM_SETCHARFORMAT, SCF_SELECTION, (LPARAM)&cf2);
355 | SendMessage (hWndCtl, EM_REPLACESEL, FALSE, (LPARAM)val);
356 | }
357 | }
358 | } fetch_total_size;
359 |
360 | DWORD
361 | __stdcall
362 | HeaderThread (LPVOID user)
363 | {
364 | auto* head =
365 | (sk_internet_head_t *)user;
366 |
367 | HINTERNET hInetRoot =
368 | InternetOpen (
369 | L"Special K Install Manager", //L"DLC Grabber",
370 | INTERNET_OPEN_TYPE_DIRECT,
371 | nullptr, nullptr,
372 | 0x00
373 | );
374 |
375 | if (! hInetRoot)
376 | goto CLEANUP;
377 |
378 | DWORD dwInetCtx;
379 |
380 | HINTERNET hInetHost =
381 | InternetConnect ( hInetRoot,
382 | head->wszHostName,
383 | INTERNET_DEFAULT_HTTP_PORT,
384 | nullptr, nullptr,
385 | INTERNET_SERVICE_HTTP,
386 | 0x00,
387 | (DWORD_PTR)&dwInetCtx );
388 |
389 | if (! hInetHost) {
390 | InternetCloseHandle (hInetRoot);
391 | goto CLEANUP;
392 | }
393 |
394 | PCWSTR rgpszAcceptTypes [] = { L"*/*", nullptr };
395 |
396 | HINTERNET hInetHTTPGetReq =
397 | HttpOpenRequest ( hInetHost,
398 | nullptr,
399 | head->wszHostPath,
400 | L"HTTP/1.1",
401 | nullptr,
402 | rgpszAcceptTypes,
403 | INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
404 | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID,
405 | (DWORD_PTR)&dwInetCtx );
406 |
407 | if (! hInetHTTPGetReq) {
408 | InternetCloseHandle (hInetHost);
409 | InternetCloseHandle (hInetRoot);
410 | goto CLEANUP;
411 | }
412 |
413 | if ( HttpSendRequestW ( hInetHTTPGetReq,
414 | nullptr,
415 | 0,
416 | nullptr,
417 | 0 ) ) {
418 |
419 | DWORD dwContentLength = 0;
420 | DWORD dwContentLength_Len = sizeof DWORD;
421 |
422 | HttpQueryInfo ( hInetHTTPGetReq,
423 | HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
424 | &dwContentLength,
425 | &dwContentLength_Len,
426 | nullptr );
427 |
428 | head->size = dwContentLength;
429 | }
430 |
431 | InternetCloseHandle (hInetHTTPGetReq);
432 | InternetCloseHandle (hInetHost);
433 | InternetCloseHandle (hInetRoot);
434 |
435 | goto END;
436 |
437 | CLEANUP:
438 | //head->size = 0;
439 |
440 | END:
441 | //CloseHandle (GetCurrentThread ());
442 |
443 | return 0;
444 | }
445 |
446 | static
447 | DWORD
448 | WINAPI
449 | DownloadThread (LPVOID user)
450 | {
451 | auto* get =
452 | (sk_internet_get_t *)user;
453 |
454 | auto ProgressMsg =
455 | [get](UINT Msg, WPARAM wParam, LPARAM lParam) ->
456 | LRESULT
457 | {
458 | if (hWndFileProgress != nullptr)
459 | return SendMessage ( hWndFileProgress,
460 | Msg,
461 | wParam,
462 | lParam );
463 | return NULL;
464 | };
465 |
466 | auto SetProgress =
467 | [=](auto cur, auto max) ->
468 | void
469 | {
470 | ProgressMsg ( PBM_SETPOS,
471 | (WPARAM)(
472 | std::numeric_limits ::max () *
473 | ((double)cur / std::max (0.0001, (double)max))
474 | ),
475 | 0L );
476 | };
477 |
478 | SetProgress (0, 0);
479 |
480 |
481 | HINTERNET hInetRoot =
482 | InternetOpen (
483 | L"Special K DLC Grabber",
484 | INTERNET_OPEN_TYPE_DIRECT,
485 | nullptr, nullptr,
486 | 0x00
487 | );
488 |
489 | if (! hInetRoot)
490 | goto CLEANUP;
491 |
492 | DWORD dwInetCtx;
493 |
494 | HINTERNET hInetHost =
495 | InternetConnect ( hInetRoot,
496 | get->wszHostName,
497 | INTERNET_DEFAULT_HTTP_PORT,
498 | nullptr, nullptr,
499 | INTERNET_SERVICE_HTTP,
500 | 0x00,
501 | (DWORD_PTR)&dwInetCtx );
502 |
503 | if (! hInetHost) {
504 | InternetCloseHandle (hInetRoot);
505 | goto CLEANUP;
506 | }
507 |
508 | PCWSTR rgpszAcceptTypes [] = { L"*/*", nullptr };
509 |
510 | HINTERNET hInetHTTPGetReq =
511 | HttpOpenRequest ( hInetHost,
512 | nullptr,
513 | get->wszHostPath,
514 | L"HTTP/1.1",
515 | nullptr,
516 | rgpszAcceptTypes,
517 | INTERNET_FLAG_NO_UI | INTERNET_FLAG_IGNORE_CERT_DATE_INVALID |
518 | INTERNET_FLAG_NO_CACHE_WRITE | INTERNET_FLAG_IGNORE_CERT_CN_INVALID,
519 | (DWORD_PTR)&dwInetCtx );
520 |
521 | if (! hInetHTTPGetReq) {
522 | InternetCloseHandle (hInetHost);
523 | InternetCloseHandle (hInetRoot);
524 | goto CLEANUP;
525 | }
526 |
527 | if ( HttpSendRequestW ( hInetHTTPGetReq,
528 | nullptr,
529 | 0,
530 | nullptr,
531 | 0 ) ) {
532 |
533 | DWORD dwContentLength = 0;
534 | DWORD dwContentLength_Len = sizeof DWORD;
535 | DWORD dwSize;
536 |
537 | HttpQueryInfo ( hInetHTTPGetReq,
538 | HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
539 | &dwContentLength,
540 | &dwContentLength_Len,
541 | nullptr );
542 |
543 | SetProgress (0, dwContentLength);
544 |
545 | DWORD dwTotalBytesDownloaded = 0UL;
546 |
547 | if ( InternetQueryDataAvailable ( hInetHTTPGetReq,
548 | &dwSize,
549 | 0x00, NULL )
550 | )
551 | {
552 | CHandle hGetFile (
553 | CreateFileW ( get->wszLocalPath,
554 | GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE,
555 | FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
556 | nullptr,
557 | CREATE_ALWAYS,
558 | FILE_ATTRIBUTE_NORMAL |
559 | FILE_FLAG_SEQUENTIAL_SCAN,
560 | nullptr ) );
561 |
562 | while (hGetFile != INVALID_HANDLE_VALUE && dwSize > 0) {
563 | if (WaitForSingleObject (hTerminateEvent, 0) == WAIT_OBJECT_0) {
564 | break;
565 | }
566 |
567 | DWORD dwRead = 0;
568 |
569 | CHeapPtr (pData);
570 |
571 | if (! pData.Allocate (dwSize))
572 | break;
573 |
574 | if ( InternetReadFile ( hInetHTTPGetReq,
575 | pData,
576 | dwSize,
577 | &dwRead )
578 | )
579 | {
580 | DWORD dwWritten = 0;
581 |
582 | WriteFile ( hGetFile,
583 | pData,
584 | dwRead,
585 | &dwWritten,
586 | nullptr );
587 |
588 | dwTotalBytesDownloaded += dwRead;
589 |
590 | SetProgress ( dwTotalBytesDownloaded, dwContentLength);
591 | SendMessage (hWndDownloadDialog, WM_FILE_PROGRESS, dwRead, 0);
592 | }
593 |
594 | if (! InternetQueryDataAvailable ( hInetHTTPGetReq,
595 | &dwSize,
596 | 0x00, NULL
597 | )
598 | ) {
599 | break;
600 | }
601 | }
602 |
603 | if (WaitForSingleObject (hTerminateEvent, 0) != WAIT_OBJECT_0)
604 | SendMessage (hWndDownloadDialog, WM_FILE_DONE, 0, 0);
605 | }
606 |
607 | get->status = STATUS_FETCHED;
608 | }
609 |
610 | InternetCloseHandle (hInetHTTPGetReq);
611 | InternetCloseHandle (hInetHost);
612 | InternetCloseHandle (hInetRoot);
613 |
614 | goto END;
615 |
616 | CLEANUP:
617 | get->status = STATUS_FAILED;
618 |
619 | END:
620 | //if (! get->hTaskDlg)
621 | //delete get;
622 |
623 | if (WaitForSingleObject (hTerminateEvent, 0) == WAIT_OBJECT_0)
624 | ResetEvent (hTerminateEvent);
625 |
626 | hWorkerThread = nullptr;
627 |
628 | CloseHandle (GetCurrentThread ());
629 |
630 | return 0;
631 | }
632 |
633 | INT_PTR
634 | CALLBACK
635 | Fetch_DlgProc (
636 | _In_ HWND hWndDlg,
637 | _In_ UINT uMsg,
638 | _In_ WPARAM wParam,
639 | _In_ LPARAM lParam )
640 | {
641 | UNREFERENCED_PARAMETER (lParam);
642 |
643 | switch (uMsg) {
644 | case WM_FILE_PROGRESS:
645 | {
646 | static uint32_t last_update = 0;
647 |
648 | total_fetched_bytes += (uint32_t)wParam;
649 | file_fetched_bytes += (uint32_t)wParam;
650 |
651 | if (timeGetTime () - last_update > 125)
652 | {
653 | wchar_t wszTotalSize [64] = { };
654 |
655 | _swprintf ( wszTotalSize,
656 | L"%7.2f MiB / %7.2f MiB",
657 | (double)total_fetched_bytes / (1024.0 * 1024.0),
658 | (double)total_fetch_size / (1024.0 * 1024.0) );
659 |
660 | fetch_op.set (RGB (22, 248, 22), L" GET ", L"Special K Install DLL");
661 | fetch_total_size.set (wszTotalSize);
662 |
663 | fetch_total_progress.set (
664 | hWndDlg,
665 | (int32_t)((double)std::numeric_limits ::max () *
666 | ((double)total_fetched_bytes / (double)total_fetch_size)) );
667 |
668 | fetch_current_file.set (file_to_fetch.wszLocalPath);
669 |
670 | wchar_t wszFileSize [64] = { L'\0' };
671 |
672 | _swprintf ( wszFileSize,
673 | L"%7.2f MiB / %7.2f MiB",
674 | (double)file_fetched_bytes / (1024.0 * 1024.0),
675 | (double)file_to_fetch.size / (1024.0 * 1024.0) );
676 |
677 | fetch_current_size.set (wszFileSize);
678 |
679 | last_update = timeGetTime ();
680 | }
681 | } break;
682 |
683 | case WM_FILE_DONE:
684 | {
685 | file_fetched_bytes = 0UL;
686 |
687 | if (! files_to_fetch.empty ())
688 | {
689 | memcpy ( &file_to_fetch,
690 | &files_to_fetch.front (),
691 | sizeof sk_internet_get_t );
692 |
693 | files_to_fetch.pop ();
694 |
695 | SKIM_Util_CreateDirectories (file_to_fetch.wszAppend);
696 | SKIM_Util_CreateDirectories (file_to_fetch.wszLocalPath);
697 |
698 | hWorkerThread =
699 | CreateThread (nullptr, 0, DownloadThread, &file_to_fetch, 0x00, nullptr);
700 | }
701 |
702 | else if (files_to_fetch.empty ())
703 | {
704 | SendMessage (hWndDlg, WM_CLOSE, 0, 0);
705 | }
706 | } break;
707 |
708 | case WM_INITDIALOG:
709 | {
710 | SetWindowTextW (hWndDlg, L"SKIM Downloader");
711 |
712 | if (hTerminateEvent != nullptr)
713 | ResetEvent (hTerminateEvent);
714 |
715 | total_fetched_bytes = 0;
716 | file_fetched_bytes = 0;
717 |
718 | total_fetch_size = 0;
719 |
720 | hWndDownloadDialog = hWndDlg;
721 |
722 | CreateWindowEx ( 0, STATUSCLASSNAME,
723 | nullptr,
724 | WS_CHILD | WS_VISIBLE |
725 | SBARS_SIZEGRIP,
726 | 0, 0, 0, 0,
727 | hWndDlg,
728 | (HMENU)IDC_STATUS,
729 | GetModuleHandle (nullptr), nullptr);
730 |
731 | fetch_total_size.hWndCtl =
732 | CreateWindowEx ( 0, MSFTEDIT_CLASS, TEXT ("CURRENT DOWNLOAD SIZE"),
733 | WS_VISIBLE | WS_CHILD | ES_READONLY |
734 | ES_RIGHT | ES_SAVESEL | WS_DISABLED,
735 | 260, 10, 193, 28,
736 | hWndDlg, nullptr, GetModuleHandle (nullptr), nullptr );
737 |
738 | fetch_op.hWndCtl =
739 | CreateWindowEx ( 0, MSFTEDIT_CLASS, TEXT ("CURRENT DOWNLOAD OP"),
740 | WS_VISIBLE | WS_CHILD | ES_READONLY |
741 | ES_LEFT | ES_SAVESEL | WS_DISABLED,
742 | 10, 10, 250, 28,
743 | hWndDlg, nullptr, GetModuleHandle (nullptr), nullptr );
744 |
745 | fetch_current_file.hWndCtl =
746 | CreateWindowEx ( 0, MSFTEDIT_CLASS, TEXT ("SINGLE FILE NAME"),
747 | WS_VISIBLE | WS_CHILD | ES_READONLY |
748 | ES_LEFT | ES_SAVESEL | WS_DISABLED,
749 | 10, 69, 250, 28,
750 | hWndDlg, nullptr, GetModuleHandle (nullptr), nullptr );
751 |
752 | fetch_current_size.hWndCtl =
753 | CreateWindowEx ( 0, MSFTEDIT_CLASS, TEXT ("SINGLE FILE SIZE"),
754 | WS_VISIBLE | WS_CHILD | ES_READONLY |
755 | ES_RIGHT | ES_SAVESEL | WS_DISABLED,
756 | 260, 69, 193, 28,
757 | hWndDlg, nullptr, GetModuleHandle (nullptr), nullptr );
758 |
759 | std::vector head_threads;
760 |
761 | for (auto& it : files_to_lookup)
762 | {
763 | it.hThread =
764 | CreateThread (
765 | nullptr,
766 | 0,
767 | HeaderThread,
768 | (LPVOID)&it,
769 | 0x00,
770 | nullptr
771 | );
772 | head_threads.push_back (it.hThread);
773 | }
774 |
775 | WaitForMultipleObjects ( (DWORD)head_threads.size (), &head_threads [0], TRUE, INFINITE );
776 |
777 | for (auto& it : files_to_lookup)
778 | {
779 | sk_internet_get_t get =
780 | files_to_fetch.front ();
781 |
782 | files_to_fetch.pop ();
783 |
784 | get.size = it.size;
785 |
786 | files_to_fetch.push (get);
787 |
788 | total_fetch_size += it.size;
789 | }
790 |
791 | hWndFileProgress =
792 | GetDlgItem (hWndDlg, IDC_DLC_FILE_PROGRESS);
793 |
794 | SendMessage (hWndFileProgress, PBM_SETSTATE, PBST_NORMAL, 0UL);
795 | SendMessage (hWndFileProgress, PBM_SETRANGE32, 0UL, std::numeric_limits ::max ());
796 | SendMessage (hWndFileProgress, PBM_SETPOS, 0, 0UL);
797 |
798 | hWndTotalProgress =
799 | GetDlgItem (hWndDlg, IDC_DLC_TOTAL_PROGRESS);
800 |
801 | SendMessage (hWndTotalProgress, PBM_SETRANGE32, 0UL, std::numeric_limits ::max ());
802 | SendMessage (hWndTotalProgress, PBM_SETPOS, 0, 0UL);
803 | SendMessage (hWndTotalProgress, PBM_SETSTATE, PBST_PAUSED, 0UL);
804 |
805 | SendMessage (hWndDlg, WM_FILE_DONE, 0, 0);
806 |
807 | return (INT_PTR)true;
808 | }
809 |
810 | case WM_CLOSE:
811 | case WM_DESTROY:
812 | {
813 | //files_to_lookup.clear ();
814 |
815 | //while (! files_to_fetch.empty ())
816 | // files_to_fetch.pop ();
817 |
818 | //while (! files_to_delete.empty ())
819 | // files_to_delete.pop ();
820 |
821 | total_fetch_size = 0;
822 | file_fetched_size = 0;
823 |
824 | DestroyWindow (fetch_op.hWndCtl);
825 | DestroyWindow (fetch_total_size.hWndCtl);
826 | DestroyWindow (fetch_current_file.hWndCtl);
827 | DestroyWindow (fetch_current_size.hWndCtl);
828 |
829 | fetch_op.set (RGB (0,0,0), L"", L"");
830 | fetch_current_file.set (L"");
831 |
832 | if (hWorkerThread != nullptr && hTerminateEvent) {
833 | SetEvent (hTerminateEvent);
834 | hWorkerThread = nullptr;
835 | }
836 |
837 | hWndDownloadDialog = nullptr;
838 | EndDialog (hWndDlg, 0x0);
839 |
840 | return (INT_PTR)true;
841 | }
842 |
843 | case WM_CREATE:
844 | case WM_PAINT:
845 | case WM_SIZE:
846 | return (INT_PTR)false;
847 | }
848 |
849 | return (INT_PTR)false;
850 | }
851 |
852 | extern HWND hWndMainDlg;
853 |
854 | DWORD
855 | __stdcall
856 | SKIM_DownloadDlg (LPVOID user)
857 | {
858 | static bool init = false;
859 |
860 | if (! init) {
861 | INITCOMMONCONTROLSEX icex;
862 | ZeroMemory (&icex, sizeof INITCOMMONCONTROLSEX);
863 |
864 | icex.dwSize = sizeof INITCOMMONCONTROLSEX;
865 | icex.dwICC = ICC_STANDARD_CLASSES | ICC_BAR_CLASSES;
866 |
867 | InitCommonControlsEx (&icex);
868 |
869 | LoadLibrary (L"Msftedit.dll");
870 |
871 | init = true;
872 | }
873 |
874 | hWndDownloadDialog =
875 | CreateDialog ( GetModuleHandle (nullptr),
876 | MAKEINTRESOURCE (IDD_DLC_INSTALL),
877 | (HWND)user,
878 | Fetch_DlgProc );
879 |
880 | MSG msg;
881 | BOOL bRet;
882 |
883 | while (hWndDownloadDialog != nullptr && (bRet = GetMessage (&msg, hWndDownloadDialog, 0, 0)) != 0)
884 | {
885 | if (bRet == -1) {
886 | return 0;
887 | }
888 |
889 | if (hWndDownloadDialog != nullptr) {
890 | TranslateMessage (&msg);
891 | DispatchMessage (&msg);
892 | }
893 | }
894 |
895 | return 0;
896 | }
897 |
898 | void
899 | SKIM_WaitForFile (const wchar_t* wszFile)
900 | {
901 | const DWORD TIMEOUT = 666UL;
902 | DWORD dwStart = timeGetTime ();
903 | HANDLE hFile = nullptr;
904 |
905 | while (hFile == nullptr)
906 | {
907 | hFile =
908 | CreateFileW ( wszFile,
909 | GENERIC_READ | GENERIC_EXECUTE,
910 | FILE_SHARE_READ | FILE_SHARE_WRITE |
911 | FILE_SHARE_DELETE,
912 | nullptr,
913 | OPEN_EXISTING,
914 | GetFileAttributes (wszFile),
915 | nullptr );
916 |
917 | if (hFile == nullptr)
918 | {
919 | if (timeGetTime () > dwStart + TIMEOUT)
920 | {
921 | MessageBox ( nullptr,
922 | L"File I/O Timed Out\r\n\r\n"
923 | L"\tPlease verify firewall and anti-virus settings",
924 | L"Install Failed",
925 | MB_ICONASTERISK );
926 | break;
927 | }
928 |
929 | SleepEx (16UL, TRUE);
930 | }
931 | }
932 |
933 | CloseHandle (hFile);
934 | }
935 |
936 |
937 | bool
938 | __stdcall
939 | SKIM_FetchInstallerDLL ( sk_product_t& product,
940 | const wchar_t *wszRemoteFile =
941 | #ifndef _WIN64
942 | L"installer.dll"
943 | #else
944 | L"installer64.dll"
945 | #endif
946 | )
947 | {
948 | SKIM_Util_MoveFileNoFail (product.wszWrapper, L"Version/wrapper.old");
949 |
950 | wchar_t wszRemoteRepoURL [INTERNET_MAX_PATH_LENGTH + 2] = { };
951 |
952 | _snwprintf ( wszRemoteRepoURL,
953 | INTERNET_MAX_PATH_LENGTH,
954 | L"/Kaldaien/SpecialK/0.8.x/%s",
955 | /*product.wszRepoName,*/ wszRemoteFile );
956 |
957 | static sk_internet_head_t head;
958 | static sk_internet_get_t get;
959 |
960 | wcsncpy (get.wszLocalPath, product.wszWrapper, MAX_PATH);
961 | wcsncpy (get.wszHostName, L"raw.githubusercontent.com", INTERNET_MAX_HOST_NAME_LENGTH);
962 | wcsncpy (get.wszHostPath, wszRemoteRepoURL, INTERNET_MAX_PATH_LENGTH);
963 |
964 | wcsncpy (head.wszHostName, L"raw.githubusercontent.com", INTERNET_MAX_HOST_NAME_LENGTH);
965 | wcsncpy (head.wszHostPath, wszRemoteRepoURL, INTERNET_MAX_PATH_LENGTH);
966 |
967 |
968 | files_to_fetch.push (get);
969 | files_to_lookup.push_back (head);
970 |
971 | SKIM_DownloadDlg (nullptr);
972 | SKIM_WaitForFile (product.wszWrapper);
973 |
974 | return true;
975 | }
976 |
977 | bool
978 | __stdcall
979 | SKIM_FetchInjectorDLL ( sk_product_t product,
980 | const wchar_t *wszRemoteFile =
981 | #ifndef _WIN64
982 | L"injector.dll"
983 | #else
984 | L"injector64.dll"
985 | #endif
986 | )
987 | {
988 | wchar_t wszOld [INTERNET_MAX_PATH_LENGTH + 2] = { };
989 | _snwprintf (wszOld, INTERNET_MAX_PATH_LENGTH, L"Version/%s.old", wszRemoteFile);
990 |
991 | SKIM_Util_MoveFileNoFail (product.wszWrapper, wszOld);
992 |
993 | wchar_t wszRemoteRepoURL [INTERNET_MAX_PATH_LENGTH + 2] = { };
994 |
995 | _snwprintf ( wszRemoteRepoURL,
996 | INTERNET_MAX_PATH_LENGTH,
997 | L"/Kaldaien/SpecialK/0.8.x/%s",
998 | /*product.wszRepoName,*/ wszRemoteFile );
999 |
1000 | static sk_internet_head_t head;
1001 | static sk_internet_get_t get;
1002 |
1003 | wcsncpy (get.wszLocalPath, product.wszWrapper, MAX_PATH);
1004 | wcsncpy (get.wszHostName, L"raw.githubusercontent.com", INTERNET_MAX_HOST_NAME_LENGTH);
1005 | wcsncpy (get.wszHostPath, wszRemoteRepoURL, INTERNET_MAX_PATH_LENGTH);
1006 |
1007 | wcsncpy (head.wszHostName, L"raw.githubusercontent.com", INTERNET_MAX_HOST_NAME_LENGTH);
1008 | wcsncpy (head.wszHostPath, wszRemoteRepoURL, INTERNET_MAX_PATH_LENGTH);
1009 |
1010 | files_to_fetch.push (get);
1011 | files_to_lookup.push_back (head);
1012 |
1013 | SKIM_DownloadDlg (nullptr);
1014 | SKIM_WaitForFile (product.wszWrapper);
1015 |
1016 | return true;
1017 | }
1018 |
1019 | bool
1020 | __stdcall
1021 | SKIM_FetchInstaller32 ( sk_product_t& product )
1022 | {
1023 | return SKIM_FetchInstallerDLL ( product, L"installer.dll" );
1024 | }
1025 |
1026 | bool
1027 | __stdcall
1028 | SKIM_FetchInstaller64 ( sk_product_t& product )
1029 | {
1030 | return SKIM_FetchInstallerDLL ( product, L"installer64.dll" );
1031 | }
1032 |
1033 |
1034 |
1035 | bool
1036 | __stdcall
1037 | SKIM_FetchInjector32 ( sk_product_t& product )
1038 | {
1039 | return SKIM_FetchInjectorDLL ( product, L"injector.dll" );
1040 | }
1041 |
1042 | bool
1043 | __stdcall
1044 | SKIM_FetchInjector64 ( sk_product_t& product )
1045 | {
1046 | return SKIM_FetchInjectorDLL ( product, L"injector64.dll" );
1047 | }
--------------------------------------------------------------------------------
/src/network.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include "product.h"
5 |
6 | bool __stdcall
7 | SKIM_FetchDLCManagerDLL ( sk_product_t& product,
8 | const wchar_t *wszRemoteFile =
9 | #ifndef _WIN64
10 | L"installer.dll"
11 | #else
12 | L"installer64.dll"
13 | #endif
14 | );
15 |
16 | bool __stdcall
17 | SKIM_FetchInstallerDLL ( sk_product_t& product,
18 | const wchar_t *wszRemoteFile =
19 | #ifndef _WIN64
20 | L"installer.dll"
21 | #else
22 | L"installer64.dll"
23 | #endif
24 | );
25 |
26 | bool __stdcall
27 | SKIM_FetchInjectorDLL ( sk_product_t& product,
28 | const wchar_t *wszRemoteFile =
29 | #ifndef _WIN64
30 | L"injector.dll"
31 | #else
32 | L"injector64.dll"
33 | #endif
34 | );
35 |
36 | DWORD __stdcall HeaderThread (LPVOID user);
37 |
38 | bool __stdcall SKIM_FetchInstaller32 (sk_product_t& product);
39 | bool __stdcall SKIM_FetchInstaller64 (sk_product_t& product);
40 |
41 | bool __stdcall SKIM_FetchInjector32 (sk_product_t& product);
42 | bool __stdcall SKIM_FetchInjector64 (sk_product_t& product);
--------------------------------------------------------------------------------
/src/product.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | enum SK_ARCHITECTURE {
4 | SK_32_BIT = 0x01,
5 | SK_64_BIT = 0x02,
6 |
7 | SK_BOTH_BIT = 0x03
8 | };
9 |
10 | struct sk_product_t {
11 | wchar_t wszWrapper [MAX_PATH];
12 | wchar_t wszPlugIn [64];
13 | wchar_t wszDLLProductName [128];
14 | wchar_t wszGameName [128];
15 | wchar_t wszProjectName [128];
16 | wchar_t wszConfigTool [128];
17 | wchar_t wszRepoName [32];
18 | wchar_t wszDonateID [16];
19 | uint32_t uiSteamAppID;
20 | SK_ARCHITECTURE architecture;
21 | bool bHasDLC;
22 | wchar_t* wszDescription;
23 |
24 | int32_t install_state; // To be filled-in later
25 |
26 | struct
27 | {
28 | HMENU hProductMenu = nullptr;
29 | HMENU hUtilMenu = nullptr;
30 | HMENU hFileMenu = nullptr;
31 | HMENU hBranchMenu = nullptr;
32 | } menus;
33 | };
--------------------------------------------------------------------------------
/src/resource.h:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/src/resource.h
--------------------------------------------------------------------------------
/src/stdafx.cpp:
--------------------------------------------------------------------------------
1 | // stdafx.cpp : source file that includes just the standard includes
2 | // tsfix_injector.pch will be the pre-compiled header
3 | // stdafx.obj will contain the pre-compiled type information
4 |
5 | #include "stdafx.h"
--------------------------------------------------------------------------------
/src/stdafx.h:
--------------------------------------------------------------------------------
1 | // stdafx.h : include file for standard system include files,
2 | // or project specific include files that are used frequently, but
3 | // are changed infrequently
4 | //
5 |
6 | #pragma once
7 |
8 | #define _CRT_NON_CONFORMING_SWPRINTFS
9 | #define _CRT_NON_CONFORMING_WCSTOK
10 | #define _CRT_SECURE_NO_WARNINGS
11 |
12 | #pragma warning (disable: 4091)
13 | #pragma warning (disable: 4723)
14 |
15 | #define NOMINMAX
16 |
17 | #include "targetver.h"
18 |
19 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
20 | #define VC_EXTRALEAN // Exclude rarely-used stuff from Windows headers
21 |
22 | // Windows Header Files:
23 | #include
24 |
25 | #include
26 |
27 | #include
28 |
29 | #include
30 | #include
31 | #include
32 |
33 | #include
34 |
35 | #include
36 |
37 | #include
38 | #include
39 | #include
40 | #include
41 |
42 | #include
43 | #include
44 |
45 | #include
46 |
47 | #pragma comment (lib, "winmm.lib" )
48 | #pragma comment (lib, "shlwapi.lib" )
49 | #pragma comment (lib, "shell32.lib" )
50 | #pragma comment (lib, "Ole32.lib" )
51 | #pragma comment (lib, "advapi32.lib")
52 | #pragma comment (lib, "user32.lib" )
53 | #pragma comment (lib, "comctl32.lib")
54 | #pragma comment (lib, "msi.lib" )
55 | #pragma comment (lib, "version.lib" )
56 |
57 | #pragma comment (linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' " \
58 | "version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df'" \
59 | " language='*'\"")
60 |
61 | #include
62 |
63 | // TODO: reference additional headers your program requires here
64 |
--------------------------------------------------------------------------------
/src/system_tray.cpp:
--------------------------------------------------------------------------------
1 | #define _CRT_SECURE_NO_WARNINGS
2 | #define _CRT_NON_CONFORMING_WCSTOK
3 | #define _CRT_NON_CONFORMING_SWPRINTFS
4 |
5 | #pragma warning (disable: 4091)
6 |
7 | #include "stdafx.h"
8 |
9 | #include "resource.h"
10 |
11 | #include
12 | #include
13 | #include
14 |
15 | #include "system_tray.h"
16 | #include "injection.h"
17 |
18 | #include "SKIM.h"
19 |
20 | #include "APP_VERSION.H"
21 |
22 | extern HINSTANCE g_hInstance;
23 |
24 | // {012BFDBD-790D-4A7B-9BC4-A2632D2569D9}
25 | static const GUID SKIM_SystemTray_UUID =
26 | { 0x12bfdbd, 0x790d, 0x4a7b, { 0x9b, 0xc4, 0xa2, 0x63, 0x2d, 0x25, 0x69, 0xd9 } };
27 |
28 | HICON hIconSKIM_Tray;
29 | static HMENU hTrayMenu = nullptr;
30 | static HMENU hTrayProductSelectMenu = nullptr;
31 | static NOTIFYICONDATAW sys_tray_icon = { };
32 |
33 | static HBITMAP hIconExit = nullptr;
34 | static HBITMAP hIconStart = nullptr;
35 | static HBITMAP hIconStop = nullptr;
36 | static HBITMAP hIconStartAtLaunch = nullptr;
37 | static HBITMAP hIconMenu = nullptr;
38 |
39 | static HBITMAP hIconBackup = nullptr;
40 | static HBITMAP hIconBranch = nullptr;
41 | static HBITMAP hIconFolder = nullptr;
42 | static HBITMAP hIconInfo = nullptr;
43 | static HBITMAP hIconInstall = nullptr;
44 | static HBITMAP hIconLogs = nullptr;
45 | static HBITMAP hIconConfig = nullptr;
46 | static HBITMAP hIconReleaseNotes = nullptr;
47 | static HBITMAP hIconUninstall = nullptr;
48 | static HBITMAP hIconUpdate = nullptr;
49 | static HBITMAP hIconUtility = nullptr;
50 |
51 | auto ApplyBitmap =
52 | [ ](HBITMAP hBitmap, UINT_PTR idx, HMENU hMenu)
53 | {
54 | MENUITEMINFOW minfo = { };
55 |
56 | minfo.cbSize = sizeof MENUITEMINFOW;
57 |
58 | GetMenuItemInfoW (hMenu, (UINT)idx, FALSE, &minfo);
59 |
60 | minfo.fMask = MIIM_BITMAP | MIIM_FTYPE;
61 | minfo.fType = MFT_STRING;
62 |
63 | minfo.hbmpItem = hBitmap;
64 |
65 | SetMenuItemInfoW (hMenu, (UINT)idx, FALSE, &minfo);
66 | };
67 |
68 | auto LoadAndApplyBitmap = [&](UINT resid, UINT_PTR idx, HMENU hMenu) ->
69 | HBITMAP
70 | {
71 | HBITMAP hBitmap = LoadBitmap (g_hInstance, MAKEINTRESOURCE (resid));
72 | ApplyBitmap (hBitmap, idx, hMenu);
73 | return hBitmap;
74 | };
75 |
76 | static POINT last_menu_pt;
77 |
78 | void
79 | SKIM_Tray_ResetMenu (void)
80 | {
81 | DestroyMenu (hTrayMenu);
82 | hTrayMenu = nullptr;
83 |
84 | DestroyMenu (hTrayProductSelectMenu);
85 | hTrayProductSelectMenu = nullptr;
86 | }
87 |
88 |
89 | HICON
90 | SKIM_GetSmallStockIcon (SHSTOCKICONID siid)
91 | {
92 | SHSTOCKICONINFO sii = { };
93 | sii.cbSize = sizeof (sii);
94 |
95 | SHGetStockIconInfo ( siid,
96 | SHGSI_ICON | SHGSI_SMALLICON | SHGFI_SELECTED,
97 | &sii );
98 |
99 | return sii.hIcon;
100 | }
101 |
102 | void
103 | SKIM_Tray_RefreshMenu (HWND hWndDlg, bool add)
104 | {
105 | sys_tray_icon.hIcon = hIconSKIM_Tray;
106 | sys_tray_icon.hBalloonIcon = hIconSKIM_Tray;
107 | sys_tray_icon.hWnd = hWndDlg;
108 | sys_tray_icon.guidItem = SKIM_SystemTray_UUID;
109 | sys_tray_icon.uFlags |= NIF_ICON | NIF_TIP | NIF_MESSAGE |
110 | /*NIF_INFO |*/ NIF_GUID | NIF_SHOWTIP;
111 |
112 | sys_tray_icon.uCallbackMessage = WM_USER | 0x0420;
113 | sys_tray_icon.uVersion = NOTIFYICON_VERSION;
114 | sys_tray_icon.dwInfoFlags |= NIIF_INFO | NIIF_LARGE_ICON | NIIF_RESPECT_QUIET_TIME;
115 |
116 |
117 | static std::wstring inject_summary;
118 | inject_summary = L"";
119 |
120 | size_t count =
121 | SKIM_SummarizeInjectedPIDs (inject_summary);
122 |
123 | if (count == 0)
124 | inject_summary += L"Special K Install Manager v " SKIM_VERSION_STR_W;
125 | else
126 | {
127 | inject_summary = std::wstring (L"Global Injection (SpecialK64.dll)") + inject_summary;
128 | }
129 |
130 | wcsncpy (sys_tray_icon.szTip, inject_summary.c_str (), 127);
131 |
132 | if (hIconExit == nullptr) { hIconExit = LoadAndApplyBitmap (IDB_EXIT, MENUCMD_EXIT, hTrayMenu); }
133 | else ApplyBitmap (hIconExit, MENUCMD_EXIT, hTrayMenu);
134 |
135 | if (SKIM_FindProductByAppID (0)->install_state == 1)
136 | {
137 | if (hIconStart == nullptr) { hIconStart = LoadAndApplyBitmap (IDB_START, MENUCMD_START_INJECTION, hTrayMenu); }
138 | else ApplyBitmap (hIconStart, MENUCMD_START_INJECTION, hTrayMenu);
139 |
140 | if (hIconStop == nullptr) { hIconStop = LoadAndApplyBitmap (IDB_STOP, MENUCMD_STOP_INJECTION, hTrayMenu); }
141 | else ApplyBitmap (hIconStop, MENUCMD_STOP_INJECTION, hTrayMenu);
142 | }
143 |
144 | if (hIconMenu == nullptr) { hIconMenu = LoadAndApplyBitmap (IDB_MENU, (UINT_PTR)hTrayProductSelectMenu, hTrayMenu); }
145 | else ApplyBitmap (hIconMenu, (UINT_PTR)hTrayProductSelectMenu, hTrayMenu);
146 |
147 | //if (hIconStartAtLaunch == 0) { hIconStartAtLaunch = LoadAndApplyIcon (IDI_STARTUP, 1, hTrayMenu); }
148 |
149 |
150 | SKIM_Tray_UpdateStartup ();
151 |
152 |
153 | if (add)
154 | Shell_NotifyIcon (NIM_ADD, &sys_tray_icon);
155 | else
156 | Shell_NotifyIcon (NIM_MODIFY, &sys_tray_icon);
157 |
158 |
159 | Shell_NotifyIcon (NIM_SETVERSION, &sys_tray_icon);
160 |
161 |
162 | RedrawWindow (hWndDlg, nullptr, nullptr, 0x00);
163 | }
164 |
165 | void
166 | SKIM_Tray_SendTo (HWND hWndDlg)
167 | {
168 | SKIM_Tray_RefreshMenu (hWndDlg, true);
169 |
170 | ShowWindow (hWndDlg, SW_HIDE);
171 | RedrawWindow (hWndDlg, nullptr, nullptr, 0x00);
172 | }
173 |
174 | void
175 | SKIM_Tray_RestoreFrom (HWND hWndDlg)
176 | {
177 | //Shell_NotifyIcon (NIM_DELETE, &sys_tray_icon);
178 |
179 | ShowWindow (hWndDlg, SW_RESTORE);
180 | }
181 |
182 |
183 | void
184 | SKIM_Tray_SetupProductMenu (sk_product_t* prod)
185 | {
186 | int idx =
187 | SKIM_GetProductIdx (prod);
188 |
189 | if (prod->menus.hProductMenu != nullptr) DestroyMenu (prod->menus.hProductMenu);
190 | if (prod->menus.hUtilMenu != nullptr) DestroyMenu (prod->menus.hUtilMenu);
191 | if (prod->menus.hBranchMenu != nullptr) DestroyMenu (prod->menus.hBranchMenu);
192 | if (prod->menus.hFileMenu != nullptr) DestroyMenu (prod->menus.hFileMenu);
193 |
194 | prod->menus.hProductMenu = CreatePopupMenu ();
195 | prod->menus.hUtilMenu = CreatePopupMenu ();
196 | prod->menus.hBranchMenu = CreatePopupMenu ();
197 | prod->menus.hFileMenu = CreatePopupMenu ();
198 |
199 | if (prod->bHasDLC)
200 | AppendMenu (prod->menus.hUtilMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_DLC, L"Manage DLC");
201 |
202 | if (wcslen (prod->wszConfigTool))
203 | AppendMenu (prod->menus.hUtilMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_CONFIG, L"Run Config Tool");
204 |
205 | AppendMenu (prod->menus.hProductMenu, MF_BYCOMMAND | MF_STRING | MF_DEFAULT, 128 * (idx+1), prod->wszProjectName);
206 |
207 | SetMenuDefaultItem (prod->menus.hProductMenu, 128 * (idx+1), FALSE);
208 |
209 | if (prod->install_state != 0)
210 | {
211 | int count =
212 | SKIM_CountProductBranches (prod);
213 |
214 | if (count > 1)
215 | {
216 | uint32_t prod_id = SKIM_BranchManager::singleton ()->getProduct ();
217 | SKIM_BranchManager::singleton ()->setProduct (prod->uiSteamAppID);
218 |
219 | std::wstring current = SKIM_BranchManager::singleton ()->getCurrentBranch ()->name;
220 |
221 | SKIM_BranchManager::singleton ()->setProduct (prod_id);
222 |
223 | for (int i = 0; i < count; i++)
224 | {
225 | SKIM_BranchManager::Branch* pBranch =
226 | SKIM_GetProductBranchByIdx ( prod, i );
227 |
228 | AppendMenu (prod->menus.hBranchMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + 96 + i, pBranch->name.c_str ());
229 |
230 | if (current == pBranch->name)
231 | {
232 | MENUITEMINFOW minfo_branch = { };
233 | minfo_branch.cbSize = sizeof MENUITEMINFOW;
234 |
235 | GetMenuItemInfoW (prod->menus.hBranchMenu, 128 * (idx+1) + 96 + i, FALSE, &minfo_branch);
236 |
237 | minfo_branch.fMask = MIIM_STATE;
238 | minfo_branch.fState |= MFS_CHECKED;
239 |
240 | SetMenuItemInfoW (prod->menus.hBranchMenu, 128 * (idx+1) + 96 + i, FALSE, &minfo_branch);
241 | }
242 | }
243 |
244 | wchar_t wszBranchSelect [MAX_PATH + 2] = { };
245 | swprintf (wszBranchSelect, L" Branch: %ws", current.c_str ());
246 |
247 | AppendMenu (prod->menus.hProductMenu, MF_POPUP | MF_STRING, (INT_PTR)prod->menus.hBranchMenu, wszBranchSelect);
248 | if (hIconBranch == nullptr) { hIconBranch = LoadAndApplyBitmap (IDB_BRANCH, (INT_PTR)prod->menus.hBranchMenu, prod->menus.hProductMenu); }
249 | else ApplyBitmap (hIconBranch, (INT_PTR)prod->menus.hBranchMenu, prod->menus.hProductMenu);
250 |
251 |
252 | AppendMenu (prod->menus.hProductMenu, MF_SEPARATOR, 0, nullptr);
253 | }
254 |
255 | //AppendMenu (prod->menus.hProductMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_RELNOTES, L" Release Notes");
256 | //if (hIconReleaseNotes == 0) { hIconReleaseNotes = LoadAndApplyBitmap (IDB_RELEASE_NOTES, 128 * (idx+1) + MENUCMD_RELNOTES, prod->menus.hProductMenu); }
257 | //else ApplyBitmap (hIconReleaseNotes, 128 * (idx+1) + MENUCMD_RELNOTES, prod->menus.hProductMenu);
258 |
259 |
260 | AppendMenu (prod->menus.hProductMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_CHECK_VERSION, L" Check Version");
261 | if (hIconUpdate == nullptr) { hIconUpdate = LoadAndApplyBitmap (IDB_UPDATE, 128 * (idx+1) + MENUCMD_CHECK_VERSION, prod->menus.hProductMenu); }
262 | else ApplyBitmap (hIconUpdate, 128 * (idx+1) + MENUCMD_CHECK_VERSION, prod->menus.hProductMenu);
263 |
264 |
265 | AppendMenu (prod->menus.hProductMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_UNINSTALL, L" Uninstall");
266 | if (hIconUninstall == nullptr) { hIconUninstall = LoadAndApplyBitmap (IDB_UNINSTALL, 128 * (idx+1) + MENUCMD_UNINSTALL, prod->menus.hProductMenu); }
267 | else ApplyBitmap (hIconUninstall, 128 * (idx+1) + MENUCMD_UNINSTALL, prod->menus.hProductMenu);
268 |
269 |
270 | //AppendMenu (prod->menus.hFileMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_DIR_ASSETS, L"Browse Assets");
271 | AppendMenu (prod->menus.hFileMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_DIR_BACKUPS,L"Browse Backups");
272 | if (hIconBackup == nullptr) { hIconBackup = LoadAndApplyBitmap (IDB_BACKUP, 128 * (idx+1) + MENUCMD_DIR_BACKUPS, prod->menus.hFileMenu); }
273 | else ApplyBitmap (hIconBackup, 128 * (idx+1) + MENUCMD_DIR_BACKUPS, prod->menus.hFileMenu);
274 |
275 | AppendMenu (prod->menus.hFileMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_DIR_CONFIG, L"Browse Config");
276 | if (hIconConfig == nullptr) { hIconConfig = LoadAndApplyBitmap (IDB_CONFIG, 128 * (idx+1) + MENUCMD_DIR_CONFIG, prod->menus.hFileMenu); }
277 | else ApplyBitmap (hIconConfig, 128 * (idx+1) + MENUCMD_DIR_CONFIG, prod->menus.hFileMenu);
278 |
279 | AppendMenu (prod->menus.hFileMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_DIR_LOGS, L"Browse Logs");
280 | if (hIconLogs == nullptr) { hIconLogs = LoadAndApplyBitmap (IDB_LOGS, 128 * (idx+1) + MENUCMD_DIR_LOGS, prod->menus.hFileMenu); }
281 | else ApplyBitmap (hIconLogs, 128 * (idx+1) + MENUCMD_DIR_LOGS, prod->menus.hFileMenu);
282 | }
283 | else
284 | {
285 | AppendMenu (prod->menus.hProductMenu, MF_BYCOMMAND | MF_STRING, 128 * (idx+1) + MENUCMD_INSTALL, L" Install");
286 | //if (hIconInstall == 0) { hIconInstall = LoadAndApplyBitmap (IDB_INSTALL, 128 * (idx+1) + MENUCMD_INSTALL, prod->menus.hProductMenu); }
287 | //else ApplyBitmap (hIconInstall, 128 * (idx+1) + MENUCMD_INSTALL, prod->menus.hProductMenu);
288 | }
289 |
290 | if (prod->install_state != 0)
291 | AppendMenu (prod->menus.hProductMenu, MF_SEPARATOR, 0, nullptr);
292 |
293 | if (prod->install_state != 0 && (prod->bHasDLC || wcslen (prod->wszConfigTool)))
294 | {
295 | AppendMenu (prod->menus.hProductMenu, MF_MOUSESELECT | MF_POPUP, (UINT_PTR)prod->menus.hUtilMenu, L" Utilities");
296 | if (hIconUtility == nullptr) { hIconUtility = LoadAndApplyBitmap (IDB_UTILS, (UINT_PTR)prod->menus.hUtilMenu, prod->menus.hProductMenu); }
297 | else ApplyBitmap (hIconUtility, (UINT_PTR)prod->menus.hUtilMenu, prod->menus.hProductMenu);
298 | }
299 |
300 | if (prod->install_state != 0)
301 | {
302 | AppendMenu (prod->menus.hProductMenu, MF_MOUSESELECT | MF_POPUP, (UINT_PTR)prod->menus.hFileMenu, L" Browse Files");
303 | if (hIconFolder == nullptr) { hIconFolder = LoadAndApplyBitmap (IDB_FOLDER, (UINT_PTR)prod->menus.hFileMenu, prod->menus.hProductMenu); }
304 | else ApplyBitmap (hIconFolder, (UINT_PTR)prod->menus.hFileMenu, prod->menus.hProductMenu);
305 | }
306 |
307 |
308 | //static HBITMAP hIconBackup = 0;
309 | //static HBITMAP hIconBranch = 0;
310 | //static HBITMAP hIconFolder = 0;
311 | //static HBITMAP hIconInfo = 0;
312 | //static HBITMAP hIconInstall = 0;
313 | //static HBITMAP hIconLogs = 0;
314 | //static HBITMAP hIconMenu = 0;
315 | //static HBITMAP hIconReleaseNotes = 0;
316 | //static HBITMAP hIconUninstall = 0;
317 | //static HBITMAP hIconUpdate = 0;
318 | //static HBITMAP hIconUtility = 0;
319 | //
320 | //
321 | //
322 | // if (hIconStart == 0) { hIconStart = LoadAndApplyBitmap (IDB_START, MENUCMD_START_INJECTION, hTrayMenu); }
323 | // else ApplyBitmap (hIconStart, MENUCMD_START_INJECTION, hTrayMenu);
324 | //
325 | // if (hIconStop == 0) { hIconStop = LoadAndApplyBitmap (IDB_STOP, MENUCMD_STOP_INJECTION, hTrayMenu); }
326 | // else ApplyBitmap (hIconStop, MENUCMD_STOP_INJECTION, hTrayMenu);
327 |
328 | }
329 |
330 | void
331 | SKIM_Tray_UpdateProduct (sk_product_t* prod)
332 | {
333 | MENUITEMINFOW minfo_product = { };
334 | minfo_product.cbSize = sizeof MENUITEMINFOW;
335 |
336 | GetMenuItemInfoW (hTrayMenu, (UINT)(UINT_PTR)hTrayProductSelectMenu, FALSE, &minfo_product);
337 |
338 | wchar_t wszProdName [256] = { };
339 |
340 | swprintf (wszProdName, L"Manage: %s", prod->wszProjectName);
341 |
342 | minfo_product.fMask = MIIM_STRING;
343 | minfo_product.fType = MIIM_STRING;
344 | minfo_product.dwTypeData = wszProdName;
345 | minfo_product.cch = (UINT)wcslen (wszProdName);
346 |
347 | SetMenuItemInfoW (hTrayMenu, (UINT)(UINT_PTR)hTrayProductSelectMenu, FALSE, &minfo_product);
348 | }
349 |
350 | void
351 | SKIM_Tray_Init (HWND hWndDlg)
352 | {
353 | if (hTrayMenu != nullptr)
354 | {
355 | DestroyMenu (hTrayMenu);
356 | hTrayMenu = nullptr;
357 | }
358 |
359 | if (hTrayMenu == nullptr)
360 | {
361 | hIconSKIM_Tray = LoadIcon (g_hInstance, MAKEINTRESOURCE (IDI_TRAY));
362 | hTrayMenu = CreatePopupMenu ();
363 | hTrayProductSelectMenu = CreatePopupMenu ();
364 |
365 | MENUINFO minfo = { };
366 | minfo.cbSize = sizeof MENUINFO;
367 | minfo.fMask = MIM_STYLE;
368 |
369 | GetMenuInfo (hTrayMenu, &minfo);
370 |
371 | minfo.dwStyle |= MNS_NOTIFYBYPOS;
372 | minfo.fMask = MIM_STYLE;
373 |
374 | std::vector prods =
375 | SKIM_GetInstallableProducts ();
376 |
377 | for ( auto& it : prods )
378 | {
379 | int idx =
380 | SKIM_GetProductIdx (it);
381 |
382 | SKIM_Tray_SetupProductMenu (it);
383 |
384 | AppendMenu (hTrayProductSelectMenu, MF_BYCOMMAND | MF_STRING | MF_MOUSESELECT | MF_POPUP, 128 * ++idx, it->wszProjectName);
385 | }
386 |
387 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_MOUSESELECT | MF_POPUP, (UINT_PTR)hTrayProductSelectMenu, L"Manage: ");
388 |
389 | if (SKIM_FindProductByAppID (0)->install_state == 1)
390 | {
391 | AppendMenu (hTrayMenu, MF_SEPARATOR, 0, nullptr);
392 |
393 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_DISABLED |
394 | MF_MOUSESELECT, 1024, L"Global Injection" );
395 |
396 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_STRING,
397 | MENUCMD_INJECT_AT_BOOT, L"Start With Windows" );
398 |
399 | AppendMenu (hTrayMenu, MF_SEPARATOR, 0, nullptr);
400 |
401 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_STRING,
402 | MENUCMD_START_INJECTION, L" Start");
403 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_STRING,
404 | MENUCMD_STOP_INJECTION, L" Stop");
405 | }
406 |
407 | AppendMenu (hTrayMenu, MF_SEPARATOR, 0, nullptr);
408 |
409 | AppendMenu (hTrayMenu, MF_BYCOMMAND | MF_STRING,
410 | MENUCMD_EXIT, L" Exit");
411 |
412 | SetMenuDefaultItem (hTrayMenu, (UINT)(UINT_PTR)hTrayProductSelectMenu, FALSE);
413 | HiliteMenuItem (hWndDlg, hTrayMenu, 0, MF_BYPOSITION | MF_UNHILITE);
414 |
415 | MENUITEMINFOW minfo_start = { },
416 | minfo_stop = { };
417 |
418 | minfo_start.cbSize = sizeof MENUITEMINFOW;
419 | minfo_stop.cbSize = sizeof MENUITEMINFOW;
420 |
421 | if (SKIM_FindProductByAppID (0)->install_state == 1)
422 | {
423 | GetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
424 | GetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
425 |
426 | if (SKIM_GetInjectorState ())
427 | {
428 | minfo_start.fMask = MIIM_STATE;
429 | minfo_start.fState = MFS_DISABLED;
430 |
431 | SetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
432 |
433 | minfo_stop.fMask = MIIM_STATE;
434 | minfo_stop.fState = MFS_ENABLED;
435 |
436 | SetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
437 | }
438 |
439 | else
440 | {
441 | minfo_start.fMask = MIIM_STATE;
442 | minfo_start.fState = MFS_ENABLED;
443 |
444 | SetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
445 |
446 | minfo_stop.fMask = MIIM_STATE;
447 | minfo_stop.fState = MFS_DISABLED;
448 |
449 | SetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
450 | }
451 | }
452 |
453 | SKIM_Tray_RefreshMenu (hWndDlg, true);
454 | }
455 | }
456 |
457 | void
458 | SKIM_Tray_RemoveFrom (void)
459 | {
460 | Shell_NotifyIcon (NIM_DELETE, &sys_tray_icon);
461 | }
462 |
463 | void
464 | SKIM_Tray_UpdateStartup (void)
465 | {
466 | MENUITEMINFOW minfo_startup = { };
467 |
468 | minfo_startup.cbSize = sizeof MENUITEMINFOW;
469 |
470 | GetMenuItemInfoW (hTrayMenu, MENUCMD_INJECT_AT_BOOT, FALSE, &minfo_startup);
471 |
472 | if (SKIM_IsLaunchedAtStartup ())
473 | {
474 | minfo_startup.fMask = MIIM_STATE;
475 | minfo_startup.fState = MFS_CHECKED | MFS_ENABLED;
476 | }
477 | else
478 | {
479 | minfo_startup.fMask = MIIM_STATE;
480 | minfo_startup.fState = MFS_UNCHECKED | MFS_ENABLED;
481 | }
482 |
483 | SetMenuItemInfoW (hTrayMenu, MENUCMD_INJECT_AT_BOOT, FALSE, &minfo_startup);
484 | }
485 |
486 | void
487 | SKIM_Tray_Stop (void)
488 | {
489 | MENUITEMINFOW minfo_start = { },
490 | minfo_stop = { };
491 |
492 | minfo_start.cbSize = sizeof MENUITEMINFOW;
493 | minfo_stop.cbSize = sizeof MENUITEMINFOW;
494 |
495 | if (SKIM_FindProductByAppID (0)->install_state == 1)
496 | {
497 | GetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
498 | GetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
499 |
500 | minfo_start.fMask = MIIM_STATE;
501 | minfo_start.fState = MFS_ENABLED;
502 |
503 | SetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
504 |
505 | minfo_stop.fMask = MIIM_STATE;
506 | minfo_stop.fState = MFS_DISABLED;
507 |
508 | SetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
509 | }
510 | }
511 |
512 | void
513 | SKIM_Tray_Start (void)
514 | {
515 | MENUITEMINFOW minfo_start = { },
516 | minfo_stop = { };
517 |
518 | minfo_start.cbSize = sizeof MENUITEMINFOW;
519 | minfo_stop.cbSize = sizeof MENUITEMINFOW;
520 |
521 | if (SKIM_FindProductByAppID (0)->install_state == 1)
522 | {
523 | GetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
524 | GetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
525 |
526 | minfo_start.fMask = MIIM_STATE;
527 | minfo_start.fState = MFS_DISABLED;
528 |
529 | SetMenuItemInfoW (hTrayMenu, MENUCMD_START_INJECTION, FALSE, &minfo_start);
530 |
531 | minfo_stop.fMask = MIIM_STATE;
532 | minfo_stop.fState = MFS_ENABLED;
533 |
534 | SetMenuItemInfoW (hTrayMenu, MENUCMD_STOP_INJECTION, FALSE, &minfo_stop);
535 | }
536 | }
537 |
538 | void
539 | SKIM_Tray_ProcessCommand (HWND hWndDlg, LPARAM lParam, WPARAM wParam)
540 | {
541 | switch (LOWORD (wParam))
542 | {
543 | //case 0:
544 | // SetMenuDefaultItem (hTrayMenu, 0, TRUE);
545 | // HiliteMenuItem (hWndDlg, hTrayMenu, 0, MF_BYPOSITION | MF_UNHILITE);
546 | // break;
547 |
548 | case MENUCMD_INJECT_AT_BOOT:
549 | {
550 | wchar_t wszExec [MAX_PATH * 2] = { };
551 | DWORD dwLen = MAX_PATH * 2 - 1;
552 |
553 | GetCurrentDirectoryW (dwLen, wszExec);
554 | PathAppend (wszExec, L"SKIM64.exe");
555 |
556 | SKIM_SetStartupInjection (SKIM_IsLaunchedAtStartup () ? false : true, wszExec);
557 | } break;
558 |
559 | // Start
560 | case MENUCMD_START_INJECTION:
561 | {
562 | SKIM_GlobalInject_Start (hWndDlg);
563 | } break;
564 |
565 | // Stop
566 | case MENUCMD_STOP_INJECTION:
567 | {
568 | SKIM_GlobalInject_Stop (hWndDlg);
569 | } break;
570 |
571 | // Exit
572 | case MENUCMD_EXIT:
573 | {
574 | //if (SKIM_GlobalInject_Stop (hWndDlg))
575 | SKIM_StopInjectingAndExit (hWndDlg);
576 | } break;
577 |
578 | // INVALID
579 | default:
580 | {
581 | if (wParam >= 128)
582 | {
583 | int idx = (INT)wParam / 128;
584 | int cmd = (wParam % 128);
585 |
586 | sk_product_t* prod = SKIM_GetProductByIdx (idx-1);
587 |
588 | extern int last_sel;
589 | last_sel = idx - 1;
590 | SendMessage (hWndDlg, WM_INITDIALOG, 0, 0);
591 | SKIM_BranchManager::singleton ()->setProduct ((uint32_t)prod->uiSteamAppID);
592 | SKIM_OnProductSelect ();
593 | SKIM_OnBranchSelect ();
594 | SendMessage (hWndDlg, WM_INITDIALOG, 0, 0);
595 |
596 | switch (cmd)
597 | {
598 | case 0:
599 | {
600 | POINT pt;
601 | GetCursorPos (&pt);
602 |
603 | BOOL ret = 128;
604 |
605 | while (ret != 0 && ret % 128 == 0)
606 | {
607 | ret = TrackPopupMenu ( prod->menus.hProductMenu,
608 | TPM_RIGHTALIGN | TPM_BOTTOMALIGN | TPM_LEFTBUTTON |
609 | TPM_VERNEGANIMATION | TPM_RETURNCMD,
610 | pt.x, pt.y,
611 | 0,
612 | hWndDlg, nullptr );
613 | }
614 |
615 | if (ret > 0)
616 | {
617 | SKIM_Tray_ProcessCommand (hWndDlg, lParam, ret);
618 | }
619 |
620 | return;
621 | } break;
622 |
623 | case MENUCMD_CHECK_VERSION:
624 | _beginthreadex (
625 | nullptr,
626 | 0,
627 | SKIM_UpdateProduct,
628 | (LPVOID)prod,
629 | 0x00,
630 | nullptr );
631 | break;
632 |
633 | case MENUCMD_UNINSTALL:
634 | SendMessage (hWndDlg, WM_COMMAND, IDC_UNINSTALL_CMD, 0);
635 | break;
636 |
637 | case MENUCMD_INSTALL:
638 | SendMessage (hWndDlg, WM_COMMAND, IDC_INSTALL_CMD, 0);
639 | break;
640 |
641 | case MENUCMD_DLC:
642 | case MENUCMD_CONFIG:
643 | SendMessage (hWndDlg, WM_COMMAND, IDC_MANAGE_CMD, 0);
644 | break;
645 |
646 | case MENUCMD_DIR_BACKUPS:
647 | {
648 | wchar_t wszVersionDir [MAX_PATH * 2] = { };
649 | lstrcatW (wszVersionDir, SKIM_FindInstallPath (prod->uiSteamAppID));
650 | PathAppendW (wszVersionDir, L"Version");
651 |
652 | ShellExecuteW (GetActiveWindow (), L"explore", wszVersionDir, nullptr, nullptr, SW_NORMAL);
653 | } break;
654 |
655 | case MENUCMD_DIR_CONFIG:
656 | {
657 | wchar_t wszConfigDir [MAX_PATH * 2] = { };
658 | lstrcatW (wszConfigDir, SKIM_FindInstallPath (prod->uiSteamAppID));
659 |
660 | ShellExecuteW (GetActiveWindow (), L"explore", wszConfigDir, nullptr, nullptr, SW_NORMAL);
661 | } break;
662 |
663 | case MENUCMD_DIR_LOGS:
664 | {
665 | wchar_t wszLogsDir [MAX_PATH * 2] = { };
666 | lstrcatW (wszLogsDir, SKIM_FindInstallPath (prod->uiSteamAppID));
667 | PathAppendW (wszLogsDir, L"logs");
668 |
669 | ShellExecuteW (GetActiveWindow (), L"explore", wszLogsDir, nullptr, nullptr, SW_NORMAL);
670 | } break;
671 |
672 | default:
673 | {
674 | if (cmd >= 96)
675 | {
676 | static HWND
677 | hWndBranchSelect =
678 | GetDlgItem (hWndDlg, IDC_BRANCH_SELECT);
679 |
680 | ComboBox_SetCurSel (hWndBranchSelect, cmd - 96);
681 |
682 | void
683 | SKIM_OnBranchSelect (void);
684 |
685 | SKIM_OnBranchSelect ();
686 |
687 | SendMessage (hWndDlg, WM_COMMAND, IDC_MIGRATE_CMD, 0);
688 | }
689 | } break;
690 | }
691 |
692 | SKIM_Tray_UpdateProduct (prod);
693 |
694 | return;
695 | }
696 | } break;
697 | }
698 |
699 | //SetCursorPos (last_menu_pt.x, last_menu_pt.y);
700 |
701 | BOOL ret =
702 | TrackPopupMenu ( hTrayMenu,
703 | TPM_LEFTALIGN | TPM_LEFTBUTTON |
704 | TPM_VERNEGANIMATION | TPM_RETURNCMD,
705 | last_menu_pt.x, last_menu_pt.y,
706 | 0,
707 | hWndDlg, nullptr );
708 |
709 | if (ret > 0)
710 | {
711 | SKIM_Tray_ProcessCommand (hWndDlg, lParam, ret);
712 | }
713 | }
714 |
715 | void
716 | SKIM_Tray_HandleContextMenu (HWND hWndDlg)
717 | {
718 | SetForegroundWindow (hWndDlg);
719 |
720 | POINT pt;
721 | GetCursorPos (&pt);
722 |
723 | last_menu_pt = pt;
724 |
725 | BOOL ret =
726 | TrackPopupMenu ( hTrayMenu,
727 | TPM_LEFTALIGN | TPM_LEFTBUTTON |
728 | TPM_VERNEGANIMATION | TPM_RETURNCMD,
729 | pt.x, pt.y,
730 | 0,
731 | hWndDlg, nullptr );
732 |
733 | if (ret > 0)
734 | {
735 | SKIM_Tray_ProcessCommand (hWndDlg, 0, ret);
736 | }
737 | }
--------------------------------------------------------------------------------
/src/system_tray.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | enum
6 | {
7 | MENUCMD_INJECT_AT_BOOT = 1,
8 | MENUCMD_START_INJECTION = 3,
9 | MENUCMD_STOP_INJECTION = 4,
10 | MENUCMD_EXIT = 6,
11 | MENUCMD_CHECK_VERSION = 17,
12 | MENUCMD_RELNOTES = 18,
13 | MENUCMD_CHANGE_BRANCH = 19,
14 | MENUCMD_DLC = 21,
15 | MENUCMD_CONFIG = 22,
16 | MENUCMD_DIR_ASSETS = 24,
17 | MENUCMD_DIR_BACKUPS = 25,
18 | MENUCMD_DIR_CONFIG = 26,
19 | MENUCMD_DIR_LOGS = 27,
20 | MENUCMD_UNINSTALL = 29,
21 | MENUCMD_INSTALL = 30
22 | };
23 |
24 | struct sk_product_t;
25 |
26 | void SKIM_Tray_RefreshMenu (HWND hWndDlg, bool add = true);
27 | void SKIM_Tray_SendTo (HWND hWndDlg);
28 | void SKIM_Tray_RestoreFrom (HWND hWndDlg);
29 | void SKIM_Tray_Init (HWND hWndDlg);
30 | void SKIM_Tray_RemoveFrom (void);
31 | void SKIM_Tray_UpdateStartup (void);
32 | void SKIM_Tray_Stop (void);
33 | void SKIM_Tray_Start (void);
34 | void SKIM_Tray_ProcessCommand (HWND hWndDlg, LPARAM lParam, WPARAM wParam);
35 | void SKIM_Tray_HandleContextMenu (HWND hWndDlg);
36 | void SKIM_Tray_UpdateProduct (sk_product_t* prod);
37 | void SKIM_Tray_SetupProductMenu (sk_product_t* prod);
38 |
39 | //TODO:
40 | //
41 | // Installed Products Sub-Menu
42 | // -------------------------
43 | // == Product Name (Version) ==
44 | // # Date Installed #
45 | // ******************
46 | //
47 | // Check for New Version
48 | // Release Notes
49 | //
50 | // Manage/Repair Sub-Menu
51 | // --------------------
52 | // DLC
53 | // Config Tool
54 | //
55 | // Installed Files Sub-Menu
56 | // ----------------------
57 | // Assets
58 | // Backups
59 | // Config
60 | // Logs
61 | //
62 | // Change Branch Sub-Menu
63 | // --------------------
64 | //
65 | // Uninstall
--------------------------------------------------------------------------------
/src/targetver.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | // Including SDKDDKVer.h defines the highest available Windows platform.
4 |
5 | // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
6 | // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
7 |
8 | #include
9 |
--------------------------------------------------------------------------------
/start.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/start.bmp
--------------------------------------------------------------------------------
/stop.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/stop.bmp
--------------------------------------------------------------------------------
/tray.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/tray.ico
--------------------------------------------------------------------------------
/uninstall.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/uninstall.bmp
--------------------------------------------------------------------------------
/update.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/update.bmp
--------------------------------------------------------------------------------
/utilities.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Kaldaien/SKIM/f973548a06ab8c012c23c5fb2593110e2b35b561/utilities.bmp
--------------------------------------------------------------------------------