├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── build.xml ├── manifest.mf ├── nbproject ├── build-impl.xml ├── genfiles.properties ├── project.properties └── project.xml └── src └── com └── steeviebops ├── hacktvgui ├── ContextMenuListener.java ├── DownloadButtonDialogue.form ├── DownloadButtonDialogue.java ├── GUI.form ├── GUI.java ├── INIFile.java ├── SatSettingsDialogue.form ├── SatSettingsDialogue.java ├── Shared.java ├── TestSettingsDialogue.form └── TestSettingsDialogue.java └── resources ├── CaptainJack └── Modes.ini ├── bandplans.ini ├── captainjack.ini ├── flatlaf.ini ├── fsphil.ini ├── fsphil └── Modes.ini └── test.gif /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=lf 2 | * text eol=lf 3 | *.tti text eol=crlf 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | 25 | 26 | **/nbproject/private/ 27 | **/nbproject/Makefile-*.mk 28 | **/nbproject/Package-*.bash 29 | build/ 30 | nbbuild/ 31 | dist/ 32 | nbdist/ 33 | .nb-gradle/ 34 | src/com/steeviebops/resources/demo.tti 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hacktv-gui - a GUI frontend application for hacktv 2 | 3 | hacktv-gui is, as the name suggests, a GUI frontend application for hacktv, which is available at https://github.com/fsphil/hacktv/ or https://github.com/captainjack64/hacktv/. It allows you to run hacktv without the use of a command line. In addition, it includes playlist support, a handler for online teletext services, and support for any streaming video site which is compatible with yt-dlp. You can also save your settings to a file and reload them later. 4 | 5 | **For Windows users, you can download an installer from here, or from the dedicated GitHub repo**.
6 | **For other systems, details on how to install and use can be found on the Getting Started wiki page.** 7 | 8 | ### Pre-requisites 9 | - A Java 11 or later runtime environment is required. Java 8 will not work. AdoptOpenJDK or Microsoft's build of OpenJDK have been tested. 10 | - To use yt-dlp features, a copy of yt-dlp is required. This can be either in your system path, or placed in the same directory as the JAR file. 11 | 12 | ### Licence information 13 | This project was originally licensed under GPL v2 until 26th April 2021. This was due to the use of GPL v2 code to capture the console output and display it on-screen. I have since replaced this code with my own work, so I have now relicensed the project to GPL v2 or later. Feel free to reuse any of this code in a GPL v3 project. 14 | 15 | ### Credits 16 | A big thank you to: 17 | 18 | - fsphil - for creating hacktv and making this possible in the first place. 19 | - Captain Jack - for expanding on fsphil's work, particularly with conditional access. 20 | - My wife Amanda - for putting up with me while working on this project! 21 | -------------------------------------------------------------------------------- /build.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Builds, tests, and runs the project hacktv-gui. 12 | 13 | 73 | 74 | -------------------------------------------------------------------------------- /manifest.mf: -------------------------------------------------------------------------------- 1 | Manifest-Version: 1.0 2 | X-COMMENT: Main-Class will be added automatically by build 3 | 4 | -------------------------------------------------------------------------------- /nbproject/genfiles.properties: -------------------------------------------------------------------------------- 1 | build.xml.data.CRC32=b280f831 2 | build.xml.script.CRC32=c539c7a0 3 | build.xml.stylesheet.CRC32=f85dc8f2@1.95.0.48 4 | # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. 5 | # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. 6 | nbproject/build-impl.xml.data.CRC32=b280f831 7 | nbproject/build-impl.xml.script.CRC32=982b865f 8 | nbproject/build-impl.xml.stylesheet.CRC32=12e0a6c2@1.101.0.48 9 | -------------------------------------------------------------------------------- /nbproject/project.properties: -------------------------------------------------------------------------------- 1 | annotation.processing.enabled=true 2 | annotation.processing.enabled.in.editor=false 3 | annotation.processing.processors.list= 4 | annotation.processing.run.all.processors=true 5 | annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output 6 | application.title=hacktv-gui 7 | application.vendor=Stephen McGarry 8 | auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml 9 | build.classes.dir=${build.dir}/classes 10 | build.classes.excludes=**/*.java,**/*.form 11 | # This directory is removed when the project is cleaned: 12 | build.dir=build 13 | build.generated.dir=${build.dir}/generated 14 | build.generated.sources.dir=${build.dir}/generated-sources 15 | # Only compile against the classpath explicitly listed here: 16 | build.sysclasspath=ignore 17 | build.test.classes.dir=${build.dir}/test/classes 18 | build.test.results.dir=${build.dir}/test/results 19 | # Uncomment to specify the preferred debugger connection transport: 20 | #debug.transport=dt_socket 21 | debug.classpath=\ 22 | ${run.classpath} 23 | debug.modulepath=\ 24 | ${run.modulepath} 25 | debug.test.classpath=\ 26 | ${run.test.classpath} 27 | debug.test.modulepath=\ 28 | ${run.test.modulepath} 29 | # Files in build.classes.dir which should be excluded from distribution jar 30 | dist.archive.excludes= 31 | # This directory is removed when the project is cleaned: 32 | dist.dir=dist 33 | dist.jar=${dist.dir}/hacktv-gui.jar 34 | dist.javadoc.dir=${dist.dir}/javadoc 35 | dist.jlink.dir=${dist.dir}/jlink 36 | dist.jlink.output=${dist.jlink.dir}/hacktv-gui 37 | endorsed.classpath= 38 | excludes= 39 | includes=** 40 | jar.archive.disabled=${jnlp.enabled} 41 | jar.compress=true 42 | jar.index=${jnlp.enabled} 43 | javac.classpath= 44 | # Space-separated list of extra javac options 45 | javac.compilerargs= 46 | javac.deprecation=false 47 | javac.external.vm=true 48 | javac.modulepath= 49 | javac.processormodulepath= 50 | javac.processorpath=\ 51 | ${javac.classpath} 52 | javac.source=11 53 | javac.target=11 54 | javac.test.classpath=\ 55 | ${javac.classpath}:\ 56 | ${build.classes.dir} 57 | javac.test.modulepath=\ 58 | ${javac.modulepath} 59 | javac.test.processorpath=\ 60 | ${javac.test.classpath} 61 | javadoc.additionalparam= 62 | javadoc.author=false 63 | javadoc.encoding=${source.encoding} 64 | javadoc.html5=false 65 | javadoc.noindex=false 66 | javadoc.nonavbar=false 67 | javadoc.notree=false 68 | javadoc.private=false 69 | javadoc.splitindex=true 70 | javadoc.use=true 71 | javadoc.version=false 72 | javadoc.windowtitle= 73 | # The jlink additional root modules to resolve 74 | jlink.additionalmodules= 75 | # The jlink additional command line parameters 76 | jlink.additionalparam= 77 | jlink.launcher=true 78 | jlink.launcher.name=hacktv-gui 79 | jnlp.codebase.type=no.codebase 80 | jnlp.descriptor=application 81 | jnlp.enabled=false 82 | jnlp.mixed.code=default 83 | jnlp.offline-allowed=false 84 | jnlp.signed=false 85 | jnlp.signing= 86 | jnlp.signing.alias= 87 | jnlp.signing.keystore= 88 | main.class=com.steeviebops.hacktvgui.GUI 89 | # Optional override of default Application-Library-Allowable-Codebase attribute identifying the locations where your signed RIA is expected to be found. 90 | manifest.custom.application.library.allowable.codebase= 91 | # Optional override of default Caller-Allowable-Codebase attribute identifying the domains from which JavaScript code can make calls to your RIA without security prompts. 92 | manifest.custom.caller.allowable.codebase= 93 | # Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed 94 | manifest.custom.codebase= 95 | # Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions) 96 | manifest.custom.permissions= 97 | manifest.file=manifest.mf 98 | meta.inf.dir=${src.dir}/META-INF 99 | mkdist.disabled=false 100 | no.dependencies=true 101 | platform.active=default_platform 102 | project.license=gpl20 103 | run.classpath=\ 104 | ${javac.classpath}:\ 105 | ${build.classes.dir} 106 | # Space-separated list of JVM arguments used when running the project. 107 | # You may also define separate properties like run-sys-prop.name=value instead of -Dname=value. 108 | # To set system properties for unit tests define test-sys-prop.name=value: 109 | run.jvmargs= 110 | run.modulepath=\ 111 | ${javac.modulepath} 112 | run.test.classpath=\ 113 | ${javac.test.classpath}:\ 114 | ${build.test.classes.dir} 115 | run.test.modulepath=\ 116 | ${javac.test.modulepath} 117 | source.encoding=UTF-8 118 | src.dir=src 119 | test.src.dir=test 120 | -------------------------------------------------------------------------------- /nbproject/project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.netbeans.modules.java.j2seproject 4 | 5 | 6 | hacktv-gui 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | Luhn 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/ContextMenuListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2021 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package com.steeviebops.hacktvgui; 20 | 21 | import java.awt.Toolkit; 22 | import java.awt.datatransfer.DataFlavor; 23 | import java.awt.event.ActionEvent; 24 | import java.awt.event.MouseAdapter; 25 | import java.awt.event.MouseEvent; 26 | import javax.swing.AbstractAction; 27 | import javax.swing.Action; 28 | import javax.swing.JPopupMenu; 29 | import javax.swing.text.JTextComponent; 30 | 31 | /** 32 | * Right-click context menu listener 33 | * @author Stephen McGarry 34 | * 35 | */ 36 | public class ContextMenuListener extends MouseAdapter { 37 | 38 | private final JPopupMenu contextMenu = new JPopupMenu(); 39 | private final Action cut; 40 | private final Action copy; 41 | private final Action paste; 42 | private final Action delete; 43 | private final Action selectAll; 44 | private JTextComponent jtc; 45 | 46 | public ContextMenuListener() { 47 | // Cut 48 | cut = new AbstractAction("Cut") { 49 | @Override 50 | public void actionPerformed(ActionEvent ae) { 51 | jtc.cut(); 52 | } 53 | }; 54 | contextMenu.add(cut); 55 | 56 | // Copy 57 | copy = new AbstractAction("Copy") { 58 | @Override 59 | public void actionPerformed(ActionEvent ae) { 60 | jtc.copy(); 61 | } 62 | }; 63 | contextMenu.add(copy); 64 | 65 | // Paste 66 | paste = new AbstractAction("Paste") { 67 | @Override 68 | public void actionPerformed(ActionEvent ae) { 69 | jtc.paste(); 70 | } 71 | }; 72 | contextMenu.add(paste); 73 | 74 | // Delete 75 | delete = new AbstractAction("Delete") { 76 | @Override 77 | public void actionPerformed(ActionEvent ae) { 78 | jtc.replaceSelection(""); 79 | } 80 | }; 81 | contextMenu.add(delete); 82 | 83 | // Separator 84 | contextMenu.addSeparator(); 85 | 86 | // Select all 87 | selectAll = new AbstractAction("Select All") { 88 | @Override 89 | public void actionPerformed(ActionEvent ae) { 90 | jtc.selectAll(); 91 | } 92 | }; 93 | contextMenu.add(selectAll); 94 | } 95 | 96 | @Override 97 | public void mouseClicked(MouseEvent evt) { 98 | if ( (evt.getButton() == MouseEvent.BUTTON3) && (evt.getSource() instanceof JTextComponent) ) { 99 | jtc = (JTextComponent) evt.getSource(); 100 | // Don't display the menu if the text component is disabled 101 | if (!jtc.isEnabled()) { 102 | evt.consume(); 103 | return; 104 | } 105 | jtc.requestFocusInWindow(); 106 | 107 | // Conditional statements to enable/disable the options 108 | boolean isEnabled = jtc.isEnabled(); 109 | boolean isEditable = jtc.isEditable(); 110 | boolean isEmpty = jtc.getText().isEmpty(); 111 | boolean isSelected = jtc.getSelectedText() != null; 112 | boolean canPaste = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null).isDataFlavorSupported(DataFlavor.stringFlavor); 113 | cut.setEnabled(isEnabled && isEditable && isSelected); 114 | copy.setEnabled(isEnabled && isSelected); 115 | paste.setEnabled(isEnabled && isEditable && canPaste); 116 | delete.setEnabled(isEnabled && !isEmpty && isSelected && isEditable); 117 | selectAll.setEnabled(isEnabled && !isEmpty); 118 | 119 | // Display the menu 120 | contextMenu.show(evt.getComponent(), evt.getX(), evt.getY()); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/DownloadButtonDialogue.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 |
213 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/DownloadButtonDialogue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | package com.steeviebops.hacktvgui; 19 | 20 | import java.awt.Cursor; 21 | import java.io.IOException; 22 | import javax.swing.JOptionPane; 23 | 24 | public class DownloadButtonDialogue extends javax.swing.JDialog { 25 | 26 | private String selection; 27 | 28 | public DownloadButtonDialogue(java.awt.Frame parent, boolean modal) { 29 | super(parent, modal); 30 | // Don't init on anything other than Windows 31 | if (System.getProperty("os.name").contains("Windows")) { 32 | initComponents(); 33 | super.setLocationRelativeTo(parent); 34 | } 35 | } 36 | 37 | /** 38 | * This method is called from within the constructor to initialize the form. 39 | * WARNING: Do NOT modify this code. The content of this method is always 40 | * regenerated by the Form Editor. 41 | */ 42 | @SuppressWarnings("unchecked") 43 | // //GEN-BEGIN:initComponents 44 | private void initComponents() { 45 | 46 | downloadPanel = new javax.swing.JPanel(); 47 | lblHeader = new javax.swing.JLabel(); 48 | btnfsphil = new javax.swing.JButton(); 49 | btnCaptainJack = new javax.swing.JButton(); 50 | btninaxeon = new javax.swing.JButton(); 51 | lblfsphil1 = new javax.swing.JLabel(); 52 | lblfsphil2 = new javax.swing.JLabel(); 53 | lblCaptainJack1 = new javax.swing.JLabel(); 54 | lblCaptainJack2 = new javax.swing.JLabel(); 55 | lblinaxeon1 = new javax.swing.JLabel(); 56 | lblinaxeon2 = new javax.swing.JLabel(); 57 | 58 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 59 | setTitle("Download hacktv for Windows"); 60 | setResizable(false); 61 | 62 | downloadPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("hacktv download options")); 63 | 64 | lblHeader.setText("Please select the hacktv build to download.
These files are hosted on an automated build server run by the author."); 65 | 66 | btnfsphil.setText("fsphil"); 67 | btnfsphil.addActionListener(new java.awt.event.ActionListener() { 68 | public void actionPerformed(java.awt.event.ActionEvent evt) { 69 | btnfsphilActionPerformed(evt); 70 | } 71 | }); 72 | 73 | btnCaptainJack.setText("Captain Jack"); 74 | btnCaptainJack.addActionListener(new java.awt.event.ActionListener() { 75 | public void actionPerformed(java.awt.event.ActionEvent evt) { 76 | btnCaptainJackActionPerformed(evt); 77 | } 78 | }); 79 | 80 | btninaxeon.setText("Matt's TV Barn"); 81 | btninaxeon.addActionListener(new java.awt.event.ActionListener() { 82 | public void actionPerformed(java.awt.event.ActionEvent evt) { 83 | btninaxeonActionPerformed(evt); 84 | } 85 | }); 86 | 87 | lblfsphil1.setText("This is the original hacktv build.
Recommended for most use cases."); 88 | 89 | lblfsphil2.setText("https://github.com/fsphil/hacktv"); 90 | lblfsphil2.addMouseListener(new java.awt.event.MouseAdapter() { 91 | public void mouseClicked(java.awt.event.MouseEvent evt) { 92 | lblfsphil2MouseClicked(evt); 93 | } 94 | public void mouseEntered(java.awt.event.MouseEvent evt) { 95 | lblfsphil2MouseEntered(evt); 96 | } 97 | public void mouseExited(java.awt.event.MouseEvent evt) { 98 | lblfsphil2MouseExited(evt); 99 | } 100 | }); 101 | 102 | lblCaptainJack1.setText("A hacktv fork with additional features and an emphasis on video scrambling."); 103 | 104 | lblCaptainJack2.setText("https://github.com/captainjack64/hacktv"); 105 | lblCaptainJack2.addMouseListener(new java.awt.event.MouseAdapter() { 106 | public void mouseClicked(java.awt.event.MouseEvent evt) { 107 | lblCaptainJack2MouseClicked(evt); 108 | } 109 | public void mouseEntered(java.awt.event.MouseEvent evt) { 110 | lblCaptainJack2MouseEntered(evt); 111 | } 112 | public void mouseExited(java.awt.event.MouseEvent evt) { 113 | lblCaptainJack2MouseExited(evt); 114 | } 115 | }); 116 | 117 | lblinaxeon1.setText("A fork of fsphil's build with support for genuine Philips test signals.
Ideal for equipment testing."); 118 | 119 | lblinaxeon2.setText("https://github.com/inaxeon/hacktv"); 120 | lblinaxeon2.addMouseListener(new java.awt.event.MouseAdapter() { 121 | public void mouseClicked(java.awt.event.MouseEvent evt) { 122 | lblinaxeon2MouseClicked(evt); 123 | } 124 | public void mouseEntered(java.awt.event.MouseEvent evt) { 125 | lblinaxeon2MouseEntered(evt); 126 | } 127 | public void mouseExited(java.awt.event.MouseEvent evt) { 128 | lblinaxeon2MouseExited(evt); 129 | } 130 | }); 131 | 132 | javax.swing.GroupLayout downloadPanelLayout = new javax.swing.GroupLayout(downloadPanel); 133 | downloadPanel.setLayout(downloadPanelLayout); 134 | downloadPanelLayout.setHorizontalGroup( 135 | downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 136 | .addGroup(downloadPanelLayout.createSequentialGroup() 137 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 138 | .addGroup(downloadPanelLayout.createSequentialGroup() 139 | .addGap(8, 8, 8) 140 | .addComponent(btnfsphil, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE) 141 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 142 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 143 | .addComponent(lblfsphil1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 144 | .addComponent(lblfsphil2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 145 | .addGroup(downloadPanelLayout.createSequentialGroup() 146 | .addGap(5, 5, 5) 147 | .addComponent(lblHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 148 | .addGroup(downloadPanelLayout.createSequentialGroup() 149 | .addGap(8, 8, 8) 150 | .addComponent(btnCaptainJack, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE) 151 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 152 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 153 | .addComponent(lblCaptainJack1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 154 | .addComponent(lblCaptainJack2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 155 | .addGroup(downloadPanelLayout.createSequentialGroup() 156 | .addGap(8, 8, 8) 157 | .addComponent(btninaxeon, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE) 158 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 159 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 160 | .addComponent(lblinaxeon1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 161 | .addComponent(lblinaxeon2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) 162 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 163 | ); 164 | downloadPanelLayout.setVerticalGroup( 165 | downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 166 | .addGroup(downloadPanelLayout.createSequentialGroup() 167 | .addGap(9, 9, 9) 168 | .addComponent(lblHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 169 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 170 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) 171 | .addComponent(btnfsphil, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) 172 | .addGroup(downloadPanelLayout.createSequentialGroup() 173 | .addComponent(lblfsphil1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 174 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 175 | .addComponent(lblfsphil2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 176 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 177 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 178 | .addComponent(btnCaptainJack, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) 179 | .addGroup(downloadPanelLayout.createSequentialGroup() 180 | .addGap(5, 5, 5) 181 | .addComponent(lblCaptainJack1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 182 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 183 | .addComponent(lblCaptainJack2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 184 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 185 | .addGroup(downloadPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 186 | .addComponent(btninaxeon, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE) 187 | .addGroup(downloadPanelLayout.createSequentialGroup() 188 | .addComponent(lblinaxeon1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 189 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 190 | .addComponent(lblinaxeon2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 191 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 192 | ); 193 | 194 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 195 | getContentPane().setLayout(layout); 196 | layout.setHorizontalGroup( 197 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 198 | .addGroup(layout.createSequentialGroup() 199 | .addContainerGap() 200 | .addComponent(downloadPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 201 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 202 | ); 203 | layout.setVerticalGroup( 204 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 205 | .addGroup(layout.createSequentialGroup() 206 | .addContainerGap() 207 | .addComponent(downloadPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 208 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 209 | ); 210 | 211 | pack(); 212 | }//
//GEN-END:initComponents 213 | 214 | private void download(int fork) { 215 | // Downloads the latest pre-compiled Windows build from my build server 216 | switch (fork) { 217 | case 0: 218 | // fsphil download 219 | selection = "https://download.bops.ie/hacktv/fsphil.zip"; 220 | break; 221 | case 1: 222 | // Captain Jack download 223 | selection = "https://download.bops.ie/hacktv/captainjack.zip"; 224 | break; 225 | case 2: 226 | // Matt's TV Barn (inaxeon) 227 | selection = "https://download.bops.ie/hacktv/mattstvbarn.zip"; 228 | break; 229 | default: 230 | break; 231 | } 232 | this.dispose(); 233 | } 234 | 235 | public String getSelection() { 236 | return selection; 237 | } 238 | 239 | private void lblinaxeon2MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblinaxeon2MouseClicked 240 | try { 241 | new Shared().launchBrowser(lblinaxeon2.getText().split("\"(.*?)")[1]); 242 | } 243 | catch (IOException e) { 244 | JOptionPane.showMessageDialog(null, "Unable to launch default browser.", GUI.APP_NAME, JOptionPane.WARNING_MESSAGE); 245 | } 246 | }//GEN-LAST:event_lblinaxeon2MouseClicked 247 | 248 | private void lblinaxeon2MouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblinaxeon2MouseEntered 249 | this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 250 | }//GEN-LAST:event_lblinaxeon2MouseEntered 251 | 252 | private void lblinaxeon2MouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblinaxeon2MouseExited 253 | this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 254 | }//GEN-LAST:event_lblinaxeon2MouseExited 255 | 256 | private void lblfsphil2MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblfsphil2MouseClicked 257 | try { 258 | new Shared().launchBrowser(lblfsphil2.getText().split("\"(.*?)")[1]); 259 | } 260 | catch (IOException e) { 261 | JOptionPane.showMessageDialog(null ,"Unable to launch default browser.", GUI.APP_NAME, JOptionPane.WARNING_MESSAGE); 262 | } 263 | }//GEN-LAST:event_lblfsphil2MouseClicked 264 | 265 | private void lblfsphil2MouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblfsphil2MouseEntered 266 | this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 267 | }//GEN-LAST:event_lblfsphil2MouseEntered 268 | 269 | private void lblfsphil2MouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblfsphil2MouseExited 270 | this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 271 | }//GEN-LAST:event_lblfsphil2MouseExited 272 | 273 | private void lblCaptainJack2MouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblCaptainJack2MouseClicked 274 | try { 275 | new Shared().launchBrowser(lblCaptainJack2.getText().split("\"(.*?)")[1]); 276 | } 277 | catch (IOException e) { 278 | JOptionPane.showMessageDialog(null ,"Unable to launch default browser.", GUI.APP_NAME, JOptionPane.WARNING_MESSAGE); 279 | } 280 | }//GEN-LAST:event_lblCaptainJack2MouseClicked 281 | 282 | private void lblCaptainJack2MouseEntered(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblCaptainJack2MouseEntered 283 | this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR)); 284 | }//GEN-LAST:event_lblCaptainJack2MouseEntered 285 | 286 | private void lblCaptainJack2MouseExited(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_lblCaptainJack2MouseExited 287 | this.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); 288 | }//GEN-LAST:event_lblCaptainJack2MouseExited 289 | 290 | private void btnfsphilActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnfsphilActionPerformed 291 | download(0); 292 | }//GEN-LAST:event_btnfsphilActionPerformed 293 | 294 | private void btnCaptainJackActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCaptainJackActionPerformed 295 | download(1); 296 | }//GEN-LAST:event_btnCaptainJackActionPerformed 297 | 298 | private void btninaxeonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btninaxeonActionPerformed 299 | download(2); 300 | }//GEN-LAST:event_btninaxeonActionPerformed 301 | 302 | // Variables declaration - do not modify//GEN-BEGIN:variables 303 | private javax.swing.JButton btnCaptainJack; 304 | private javax.swing.JButton btnfsphil; 305 | private javax.swing.JButton btninaxeon; 306 | private javax.swing.JPanel downloadPanel; 307 | private javax.swing.JLabel lblCaptainJack1; 308 | private javax.swing.JLabel lblCaptainJack2; 309 | private javax.swing.JLabel lblHeader; 310 | private javax.swing.JLabel lblfsphil1; 311 | private javax.swing.JLabel lblfsphil2; 312 | private javax.swing.JLabel lblinaxeon1; 313 | private javax.swing.JLabel lblinaxeon2; 314 | // End of variables declaration//GEN-END:variables 315 | } 316 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/INIFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | 19 | package com.steeviebops.hacktvgui; 20 | 21 | import java.io.File; 22 | import java.io.IOException; 23 | import java.io.Serializable; 24 | import java.nio.charset.StandardCharsets; 25 | import java.nio.file.Files; 26 | import java.util.ArrayList; 27 | import java.util.Locale; 28 | import java.util.regex.Pattern; 29 | import java.util.regex.Matcher; 30 | import java.util.stream.Collectors; 31 | import java.util.stream.Stream; 32 | 33 | /** 34 | * Basic INI file reader/writer. 35 | * Uses regular expressions to read/write the contents of an INI file. 36 | * @author Stephen McGarry 37 | * 38 | * To use this, provide a string containing the path to your INI file, or a 39 | * string containing the contents of a file. The getINIValue function will 40 | * detect what you have provided and act accordingly. 41 | * 42 | * Comments are allowed on their own lines or in-line with values or section names. 43 | * 44 | */ 45 | 46 | public class INIFile implements Serializable { 47 | 48 | private static final long serialVersionUID = -2053155283451566314L; 49 | 50 | /** 51 | * Returns a Boolean of the specified INI setting. This can be true, false, 1, or 0. 52 | * If the value was not found, a default value of false is returned. 53 | * 54 | * @param input A string containing the contents of the INI file, or its path 55 | * @param section The section of the INI file that you want to look up 56 | * @param setting The setting that you want to look up 57 | * @return Returns a Boolean of the specified INI setting or a default value of false 58 | */ 59 | public boolean getBooleanFromINI(String input, String section, String setting) { 60 | String v = getINIValue(input, section, setting, "").toLowerCase(Locale.ENGLISH); 61 | switch (v) { 62 | case "0": 63 | case "false": 64 | return false; 65 | case "1": 66 | case "true": 67 | return true; 68 | default: 69 | return false; 70 | } 71 | } 72 | 73 | /** 74 | * Returns an integer of the specified INI setting. 75 | * 76 | * @param input A string containing the contents of the INI file, or its path 77 | * @param section The section of the INI file that you want to look up 78 | * @param setting The setting that you want to look up 79 | * @return An integer value if found, or null if it was not 80 | */ 81 | public Integer getIntegerFromINI(String input, String section, String setting) { 82 | try { 83 | return Integer.parseInt(getINIValue(input, section, setting, "")); 84 | } 85 | catch (NumberFormatException e) { 86 | return null; 87 | } 88 | } 89 | 90 | /** 91 | * Returns a double of the specified INI setting. 92 | * 93 | * @param input A string containing the contents of the INI file, or its path 94 | * @param section The section of the INI file that you want to look up 95 | * @param setting The setting that you want to look up 96 | * @return A double value if found, or null if it was not 97 | */ 98 | public Double getDoubleFromINI(String input, String section, String setting) { 99 | try { 100 | return Double.parseDouble(getINIValue(input, section, setting, "")); 101 | } 102 | catch (NumberFormatException e) { 103 | return null; 104 | } 105 | } 106 | 107 | /** 108 | * Returns a long of the specified INI setting. 109 | * 110 | * @param input A string containing the contents of the INI file, or its path 111 | * @param section The section of the INI file that you want to look up 112 | * @param setting The setting that you want to look up 113 | * @return A long value if found, or null if it was not 114 | */ 115 | public Long getLongFromINI(String input, String section, String setting) { 116 | try { 117 | return Long.parseLong(getINIValue(input, section, setting, "")); 118 | } 119 | catch (NumberFormatException e) { 120 | return null; 121 | } 122 | } 123 | 124 | /** 125 | * Returns the specified INI setting as a string value, with the option of altering case. 126 | * 127 | * @param input A string containing the contents of the INI file, or its path 128 | * @param section The section of the INI file that you want to look up 129 | * @param setting The setting that you want to look up 130 | * @param defaultValue The default value to return if the setting does not exist 131 | * @param preserveCase If true, returns the setting as specified in the INI file. If false, returns the setting in lower case. 132 | * @return Returns the value of the specified setting, or the specified default value if it does not exist. 133 | */ 134 | public String getStringFromINI(String input, String section, String setting, String defaultValue, boolean preserveCase) { 135 | if (!preserveCase) { 136 | return getINIValue(input, section, setting, defaultValue).toLowerCase(Locale.ENGLISH); 137 | } 138 | else { 139 | return getINIValue(input, section, setting, defaultValue); 140 | } 141 | } 142 | 143 | /** 144 | * Returns the specified INI setting as a string value. 145 | * 146 | * @param input A string containing the contents of the INI file, or its path 147 | * @param section The section of the INI file that you want to look up 148 | * @param setting The setting that you want to look up 149 | * @param defaultValue The default value to return if the setting does not exist 150 | * @return Returns the value of the specified setting, or the specified default value if it does not exist. 151 | */ 152 | public String getINIValue(String input, String section, String setting, String defaultValue) { 153 | String fileContents; 154 | // If the setting string contains a backslash, square brackets or equals, abort immediately. 155 | if ((setting.contains("\\")) || (setting.contains("[")) || 156 | (setting.contains("]")) || (setting.contains("=")) ) { 157 | throw new IllegalArgumentException("INI setting cannot contain a backslash, square brackets or equals symbol."); 158 | } 159 | // If the input string contains one line, treat it as a file path 160 | // Otherwise, treat it as the contents of an INI file 161 | long lines = input.chars().filter(x -> x == '\n').count() + 1; 162 | if (lines == 1) { 163 | // Load the specified file to a string named fileContents 164 | var f = new File(input); 165 | try { 166 | fileContents = Files.readString(f.toPath(), StandardCharsets.UTF_8); 167 | } 168 | catch (IOException e) { 169 | return defaultValue; 170 | } 171 | } 172 | else { 173 | // Set the fileContents variable to whatever we received from input 174 | fileContents = input; 175 | } 176 | // Remove all CR characters 177 | fileContents = fileContents.replaceAll("\r\n", "\n"); 178 | fileContents = fileContents.replaceAll("\r", "\n"); 179 | // Remove any comment lines so they don't interfere with processing 180 | fileContents = Stream.of(fileContents.split("\n")) 181 | .filter(s -> !s.startsWith(";")) 182 | .collect(Collectors.joining("\n")); 183 | // Remove any white spaces between the setting and value 184 | fileContents = fileContents.replaceAll("[^\\S\n]([=])[^\\S\n]", "="); 185 | // Extract the specified section from fileContents to parsedSection 186 | String parsedSection = null; 187 | String r1 = "(?ms)^\\["; 188 | String r2 = "](?:(?!^\\[[^]\\n]+]).)*"; 189 | Pattern pattern = Pattern.compile(r1 + section + r2); 190 | Matcher matcher = pattern.matcher(fileContents); 191 | while (matcher.find()) { 192 | parsedSection = matcher.group(0); 193 | } 194 | 195 | // If the specified section was not found, return the default value 196 | if (parsedSection == null) return defaultValue; 197 | 198 | /* Check for special characters in the provided setting. 199 | * If found, we need to escape them. 200 | * We do this by checking for each character in the string 'sc' below. 201 | */ 202 | String ps; 203 | String sc = "!@#$%&*()'+,-./:<>?^_`{|}"; 204 | for (int i = 0; i < setting.length(); i++) { 205 | if (i != setting.length() - 1) { 206 | // Extract a single character from 'settings' to 'ps' 207 | ps = setting.substring(i, i + 1); 208 | } 209 | else { 210 | // If we have reached the last character in the sequence, don't 211 | // try to read the next one as we'll overflow. 212 | ps = setting.substring(i); 213 | } 214 | for (int x = 0; x < sc.length(); x++) { 215 | // If 'ps' matches one of the characters in 'sc' but is not a backslash 216 | if ((ps.equals(sc.substring(x, x + 1)) && (i > 0))) { 217 | if (!setting.substring(i - 1, i).equals("\\")) { 218 | // Match found, append escape character 219 | setting = setting.replace(ps, "\\" + ps); 220 | } 221 | } 222 | else if ((ps.equals(sc.substring(x, x + 1)) && (i == 0))) { 223 | if (!setting.substring(i).equals("\\")) { 224 | // Match found, append escape character 225 | setting = setting.replace(ps, "\\" + ps); 226 | } 227 | } 228 | } 229 | } 230 | 231 | // Extract the required setting from parsedSection and return its value 232 | // If value is null, return the specified default value instead 233 | String result = null; 234 | 235 | String regex1 = "(?i)(?<=^"; 236 | String regex2 = "=)[^\n]*"; 237 | 238 | Pattern p = Pattern.compile(regex1 + setting + regex2, Pattern.MULTILINE); 239 | Matcher m = p.matcher(parsedSection); 240 | 241 | while (m.find()) { 242 | if (result == null) result = m.group(0); 243 | } 244 | if (result != null) { 245 | // Return the result, minus any in-line comments and with 246 | // leading and/or trailing whitespaces removed. 247 | return result.replaceAll(";.*", "").trim(); 248 | } 249 | else { 250 | return defaultValue; 251 | } 252 | } 253 | 254 | /** 255 | * Adds a boolean setting to a provided INI string. 256 | * 257 | * @param fileContents The full INI file contents 258 | * @param section The section that you want to write to 259 | * @param setting The setting that you want to write to 260 | * @param value The new value of the setting 261 | * @return Returns fileContents with the new setting added or changed 262 | */ 263 | public String setBooleanINIValue (String fileContents, String section, String setting, boolean value) { 264 | String stringValue; 265 | if (value == true) { 266 | stringValue = "true"; 267 | } 268 | else { 269 | stringValue = "false"; 270 | } 271 | return setINIValue(fileContents, section, setting, stringValue); 272 | } 273 | 274 | /** 275 | * Adds an integer setting to a provided INI string. 276 | * 277 | * @param fileContents The full INI file contents 278 | * @param section The section that you want to write to 279 | * @param setting The setting that you want to write to 280 | * @param value The new value of the setting 281 | * @return Returns fileContents with the new setting added or changed 282 | */ 283 | public String setIntegerINIValue (String fileContents, String section, String setting, Integer value) { 284 | return setINIValue(fileContents, section, setting, value.toString()); 285 | } 286 | 287 | /** 288 | * Adds a double setting to a provided INI string. 289 | * 290 | * @param fileContents The full INI file contents 291 | * @param section The section that you want to write to 292 | * @param setting The setting that you want to write to 293 | * @param value The new value of the setting 294 | * @return Returns fileContents with the new setting added or changed 295 | */ 296 | public String setDoubleINIValue (String fileContents, String section, String setting, Double value) { 297 | return setINIValue(fileContents, section, setting, value.toString()); 298 | } 299 | 300 | /** 301 | * Adds a long setting to a provided INI string. 302 | * 303 | * @param fileContents The full INI file contents 304 | * @param section The section that you want to write to 305 | * @param setting The setting that you want to write to 306 | * @param value The new value of the setting 307 | * @return Returns fileContents with the new setting added or changed 308 | */ 309 | public String setLongINIValue (String fileContents, String section, String setting, Long value) { 310 | return setINIValue(fileContents, section, setting, value.toString()); 311 | } 312 | 313 | /** 314 | * Adds a setting to a provided INI string. 315 | * 316 | * @param fileContents The full INI file contents 317 | * @param section The section that you want to write to 318 | * @param setting The setting that you want to write to 319 | * @param value The new value of the setting 320 | * @return Returns fileContents with the new setting added or changed 321 | */ 322 | public String setINIValue(String fileContents, String section, String setting, String value) { 323 | /** 324 | * The way we save a file is as follows: 325 | * - Feed a string into this method using the first parameter 326 | * - This string is loaded to a variable named fileContents 327 | * - Use a regex to retrieve the section names to an ArrayList 328 | * - Use another regex to split the file into its sections 329 | * - Query the first ArrayList for the specified section 330 | * - If found, retrieve the requested section 331 | * - If not found, create a new entry in the lists 332 | * - Append the value to the requested section 333 | * - Merge all sections and return a string with the new file contents 334 | * 335 | */ 336 | 337 | // If a blank string was specified, add the section provided to 338 | // create the contents of a new "file" 339 | if (fileContents.isBlank()) fileContents = "[" + section + "]\n"; 340 | 341 | // Remove any CR characters 342 | fileContents = fileContents.replaceAll("\r\n", "\n"); 343 | fileContents = fileContents.replaceAll("\r", "\n"); 344 | 345 | // Retrieve the requested section 346 | String selectedSection = splitINIfile(fileContents, section); 347 | 348 | // Retrieve all INI section names 349 | String r = "^\\[[^\\]\\n]+]"; 350 | Pattern p = Pattern.compile(r, Pattern.MULTILINE); 351 | Matcher m = p.matcher(fileContents); 352 | // Add the results to an ArrayList so we can read it later 353 | var iniSectionNames = new ArrayList(); 354 | while (m.find()) { 355 | iniSectionNames.add(m.group().substring(1,m.group().length() -1)); 356 | } 357 | 358 | // Retrieve all sections, including data 359 | var allSections = new ArrayList(); 360 | for (int i = 0; i < iniSectionNames.size(); i++) { 361 | allSections.add(splitINIfile(fileContents, iniSectionNames.get(i))); 362 | } 363 | 364 | // Query the first ArrayList for the specified section 365 | for (int i = 0; i < iniSectionNames.size(); i++) { 366 | if (selectedSection == null) { 367 | // If not found, create a new section entry in the lists 368 | iniSectionNames.add(section); 369 | selectedSection = "\n[" + section + "]" + "\n" + setting + "=" + value + "\n"; 370 | allSections.add(selectedSection); 371 | break; 372 | } 373 | else if ( ( iniSectionNames.get(i).equals(section) ) ) { 374 | // If found, retrieve the requested section 375 | selectedSection = selectedSection.trim() + "\n" + setting + "=" + value + "\n"; 376 | allSections.set(i, selectedSection); 377 | } 378 | } 379 | 380 | // Merge all sections using a StringBuilder 381 | var sb = new StringBuilder(); 382 | for (String s : allSections) { 383 | sb.append(s.replace("\n\n", "\n")); 384 | } 385 | 386 | // Return the contents of the StringBuffer 387 | // Also make it look nicer by adding an empty line betwen sections 388 | return sb.toString().replaceAll("\n\\[", "\n\n\\["); 389 | 390 | } 391 | 392 | /** 393 | * Returns a single section of an INI file 394 | * @param fileContents The contents of the INI file that you want to split 395 | * @param section The section that you want to extract from the file 396 | * @return Returns the section specified 397 | */ 398 | public String splitINIfile(String fileContents, String section) { 399 | String selectedSection = null; 400 | // Remove any CR characters 401 | fileContents = fileContents.replace("\r\n", "\n"); 402 | fileContents = fileContents.replace("\r", "\n"); 403 | // Remove any comment lines so they don't interfere with processing 404 | fileContents = Stream.of(fileContents.split("\n")) 405 | .filter(s -> !s.startsWith(";")) 406 | .collect(Collectors.joining("\n")); 407 | // Extract the specified section from fileContents 408 | String r1 = "^\\["; 409 | String r2 = "](?:\\n(?:[^\\[\\n].*)?)*"; 410 | Pattern p1 = Pattern.compile(r1 + section + r2, Pattern.MULTILINE); 411 | Matcher m1 = p1.matcher(fileContents); 412 | // Add the result to a string 413 | while (m1.find()) { 414 | selectedSection = m1.group(); 415 | } 416 | // Return the string we created 417 | return selectedSection; 418 | } 419 | 420 | } 421 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/SatSettingsDialogue.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 |
284 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/SatSettingsDialogue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2024 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | package com.steeviebops.hacktvgui; 19 | 20 | import javax.swing.JOptionPane; 21 | 22 | public class SatSettingsDialogue extends javax.swing.JDialog { 23 | 24 | private boolean changed; 25 | 26 | /** 27 | * This method is called from within the constructor to initialize the form. 28 | * WARNING: Do NOT modify this code. The content of this method is always 29 | * regenerated by the Form Editor. 30 | */ 31 | @SuppressWarnings("unchecked") 32 | // //GEN-BEGIN:initComponents 33 | private void initComponents() { 34 | 35 | harmonicBG = new javax.swing.ButtonGroup(); 36 | satSettingsPanel = new javax.swing.JPanel(); 37 | lblLO = new javax.swing.JLabel(); 38 | lblHelpText = new javax.swing.JLabel(); 39 | txtLO = new javax.swing.JTextField(); 40 | harmonicPanel = new javax.swing.JPanel(); 41 | radFirst = new javax.swing.JRadioButton(); 42 | radSecond = new javax.swing.JRadioButton(); 43 | radThird = new javax.swing.JRadioButton(); 44 | radFourth = new javax.swing.JRadioButton(); 45 | chkApplyToCustom = new javax.swing.JCheckBox(); 46 | lblRxDevice = new javax.swing.JLabel(); 47 | cmbRxDevice = new javax.swing.JComboBox<>(); 48 | chkShowRealFreq = new javax.swing.JCheckBox(); 49 | buttonPanel = new javax.swing.JPanel(); 50 | btnOK = new javax.swing.JButton(); 51 | btnCancel = new javax.swing.JButton(); 52 | 53 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 54 | setTitle("Satellite receiver settings"); 55 | setName("SatSettings"); // NOI18N 56 | setResizable(false); 57 | 58 | satSettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("LNB settings")); 59 | 60 | lblLO.setText("LNB local oscillator (GHz)"); 61 | 62 | lblHelpText.setText("Enter the LNB settings from your satellite receiver.
Some band plans may override this setting.
See the wiki page for more information."); 63 | 64 | txtLO.setColumns(6); 65 | txtLO.addMouseListener(new ContextMenuListener()); 66 | 67 | harmonicPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Harmonic settings")); 68 | 69 | harmonicBG.add(radFirst); 70 | radFirst.setText("Standard IF (first harmonic)"); 71 | radFirst.setToolTipText(""); 72 | radFirst.setActionCommand("1"); // NOI18N 73 | 74 | harmonicBG.add(radSecond); 75 | radSecond.setText("Second"); 76 | radSecond.setActionCommand("2"); // NOI18N 77 | 78 | harmonicBG.add(radThird); 79 | radThird.setText("Third"); 80 | radThird.setActionCommand("3"); // NOI18N 81 | 82 | harmonicBG.add(radFourth); 83 | radFourth.setText("Fourth"); 84 | radFourth.setActionCommand("4"); // NOI18N 85 | 86 | javax.swing.GroupLayout harmonicPanelLayout = new javax.swing.GroupLayout(harmonicPanel); 87 | harmonicPanel.setLayout(harmonicPanelLayout); 88 | harmonicPanelLayout.setHorizontalGroup( 89 | harmonicPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 90 | .addGroup(harmonicPanelLayout.createSequentialGroup() 91 | .addContainerGap() 92 | .addGroup(harmonicPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 93 | .addComponent(radFirst) 94 | .addComponent(radSecond) 95 | .addComponent(radThird) 96 | .addComponent(radFourth)) 97 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 98 | ); 99 | harmonicPanelLayout.setVerticalGroup( 100 | harmonicPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 101 | .addGroup(harmonicPanelLayout.createSequentialGroup() 102 | .addContainerGap() 103 | .addComponent(radFirst) 104 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 105 | .addComponent(radSecond) 106 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 107 | .addComponent(radThird) 108 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 109 | .addComponent(radFourth) 110 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 111 | ); 112 | 113 | chkApplyToCustom.setText("Apply these settings to custom frequencies"); 114 | chkApplyToCustom.addActionListener(new java.awt.event.ActionListener() { 115 | public void actionPerformed(java.awt.event.ActionEvent evt) { 116 | chkApplyToCustomActionPerformed(evt); 117 | } 118 | }); 119 | 120 | lblRxDevice.setText("Reception device"); 121 | 122 | cmbRxDevice.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Direct (IF)", "Ku-band LNB", "Ku LNB with BSB box", "Saorsat Ka LNB" })); 123 | cmbRxDevice.addMouseWheelListener(new java.awt.event.MouseWheelListener() { 124 | public void mouseWheelMoved(java.awt.event.MouseWheelEvent evt) { 125 | cmbRxDeviceMouseWheelMoved(evt); 126 | } 127 | }); 128 | cmbRxDevice.addActionListener(new java.awt.event.ActionListener() { 129 | public void actionPerformed(java.awt.event.ActionEvent evt) { 130 | cmbRxDeviceActionPerformed(evt); 131 | } 132 | }); 133 | 134 | chkShowRealFreq.setText("Show real output frequency"); 135 | chkShowRealFreq.addActionListener(new java.awt.event.ActionListener() { 136 | public void actionPerformed(java.awt.event.ActionEvent evt) { 137 | chkShowRealFreqActionPerformed(evt); 138 | } 139 | }); 140 | 141 | javax.swing.GroupLayout satSettingsPanelLayout = new javax.swing.GroupLayout(satSettingsPanel); 142 | satSettingsPanel.setLayout(satSettingsPanelLayout); 143 | satSettingsPanelLayout.setHorizontalGroup( 144 | satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 145 | .addGroup(satSettingsPanelLayout.createSequentialGroup() 146 | .addGroup(satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 147 | .addComponent(harmonicPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 148 | .addGroup(satSettingsPanelLayout.createSequentialGroup() 149 | .addContainerGap() 150 | .addGroup(satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 151 | .addComponent(lblHelpText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 152 | .addGroup(satSettingsPanelLayout.createSequentialGroup() 153 | .addComponent(lblLO) 154 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 155 | .addComponent(txtLO, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 156 | .addGroup(satSettingsPanelLayout.createSequentialGroup() 157 | .addComponent(lblRxDevice) 158 | .addGap(18, 18, 18) 159 | .addComponent(cmbRxDevice, javax.swing.GroupLayout.PREFERRED_SIZE, 133, javax.swing.GroupLayout.PREFERRED_SIZE)) 160 | .addComponent(chkShowRealFreq) 161 | .addComponent(chkApplyToCustom)) 162 | .addGap(0, 0, Short.MAX_VALUE))) 163 | .addContainerGap()) 164 | ); 165 | satSettingsPanelLayout.setVerticalGroup( 166 | satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 167 | .addGroup(satSettingsPanelLayout.createSequentialGroup() 168 | .addComponent(lblHelpText, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 169 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 170 | .addGroup(satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 171 | .addComponent(lblLO) 172 | .addComponent(txtLO, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 173 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 174 | .addComponent(chkApplyToCustom) 175 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 176 | .addComponent(chkShowRealFreq) 177 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 178 | .addGroup(satSettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 179 | .addComponent(lblRxDevice) 180 | .addComponent(cmbRxDevice, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 181 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 182 | .addComponent(harmonicPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 183 | ); 184 | 185 | btnOK.setText("OK"); 186 | btnOK.addActionListener(new java.awt.event.ActionListener() { 187 | public void actionPerformed(java.awt.event.ActionEvent evt) { 188 | btnOKActionPerformed(evt); 189 | } 190 | }); 191 | buttonPanel.add(btnOK); 192 | 193 | btnCancel.setText("Cancel"); 194 | btnCancel.addActionListener(new java.awt.event.ActionListener() { 195 | public void actionPerformed(java.awt.event.ActionEvent evt) { 196 | btnCancelActionPerformed(evt); 197 | } 198 | }); 199 | buttonPanel.add(btnCancel); 200 | 201 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 202 | getContentPane().setLayout(layout); 203 | layout.setHorizontalGroup( 204 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 205 | .addGroup(layout.createSequentialGroup() 206 | .addContainerGap() 207 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 208 | .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 209 | .addComponent(satSettingsPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 210 | .addContainerGap()) 211 | ); 212 | layout.setVerticalGroup( 213 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 214 | .addGroup(layout.createSequentialGroup() 215 | .addContainerGap() 216 | .addComponent(satSettingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 217 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 218 | .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 219 | .addContainerGap()) 220 | ); 221 | 222 | pack(); 223 | }//
//GEN-END:initComponents 224 | 225 | public SatSettingsDialogue(java.awt.Frame parent, boolean modal) { 226 | super(parent, modal); 227 | initComponents(); 228 | super.setLocationRelativeTo(parent); 229 | // Load preferences or use defaults if none defined 230 | Double lo = GUI.PREFS.getDouble("localoscillator", GUI.DEFAULT_LO); 231 | txtLO.setText(lo.toString()); 232 | // Get RX device setting 233 | int rx = GUI.PREFS.getInt("rxdevice", 0); 234 | if ( (rx >= 0) && (rx < cmbRxDevice.getItemCount()) ) { 235 | cmbRxDevice.setSelectedIndex(rx); 236 | } 237 | // Get checkbox preferences 238 | if (GUI.PREFS.getInt("showrealfrequency", 0) == 1) { 239 | chkShowRealFreq.doClick(); 240 | } 241 | else if (GUI.PREFS.getInt("applyloforcustomfreq", 0) == 1) { 242 | chkApplyToCustom.setSelected(true); 243 | } 244 | // Get harmonic setting 245 | switch (GUI.PREFS.getInt("harmonic", 1)) { 246 | case 1: 247 | default: 248 | if (radFirst.isEnabled()) radFirst.setSelected(true); 249 | break; 250 | case 2: 251 | if (radSecond.isEnabled()) radSecond.setSelected(true); 252 | break; 253 | case 3: 254 | if (radThird.isEnabled()) radThird.setSelected(true); 255 | break; 256 | case 4: 257 | if (radFourth.isEnabled()) radFourth.setSelected(true); 258 | break; 259 | } 260 | // Set focus on Cancel button 261 | btnCancel.requestFocus(); 262 | } 263 | 264 | public boolean settingsChanged() { 265 | return changed; 266 | } 267 | 268 | private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed 269 | this.dispose(); 270 | }//GEN-LAST:event_btnCancelActionPerformed 271 | 272 | private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed 273 | // Commit local oscillator to preferences 274 | String invalidLO = "Please enter a numeric or decimal value between 5 and 30 GHz."; 275 | try { 276 | double d = Double.parseDouble(txtLO.getText()); 277 | if ( (d < 5.0) || (d > 30.0) ) { 278 | JOptionPane.showMessageDialog(null, invalidLO, GUI.APP_NAME, JOptionPane.WARNING_MESSAGE); 279 | return; 280 | } 281 | else if (Math.abs(d - GUI.DEFAULT_LO) < 0.000001) { 282 | GUI.PREFS.remove("localoscillator"); 283 | } 284 | else { 285 | GUI.PREFS.putDouble("localoscillator", d); 286 | } 287 | } 288 | catch (NumberFormatException nfe) { 289 | JOptionPane.showMessageDialog(null, invalidLO, GUI.APP_NAME, JOptionPane.WARNING_MESSAGE); 290 | return; 291 | } 292 | // Commit harmonic to preferences 293 | switch (harmonicBG.getSelection().getActionCommand()) { 294 | case "1": 295 | GUI.PREFS.remove("harmonic"); 296 | break; 297 | default: 298 | GUI.PREFS.put("harmonic", harmonicBG.getSelection().getActionCommand()); 299 | break; 300 | } 301 | // Commit checkboxes to preferences 302 | if (chkApplyToCustom.isSelected()) { 303 | GUI.PREFS.putInt("applyloforcustomfreq", 1); 304 | } 305 | else { 306 | GUI.PREFS.remove("applyloforcustomfreq"); 307 | } 308 | if (chkShowRealFreq.isSelected()) { 309 | GUI.PREFS.putInt("showrealfrequency", 1); 310 | } 311 | else { 312 | GUI.PREFS.remove("showrealfrequency"); 313 | } 314 | // Commit RX device to preferences 315 | switch (cmbRxDevice.getSelectedIndex()) { 316 | case 0: 317 | GUI.PREFS.remove("rxdevice"); 318 | break; 319 | default: 320 | GUI.PREFS.putInt("rxdevice", cmbRxDevice.getSelectedIndex()); 321 | break; 322 | } 323 | // Let the parent window know that we changed something... 324 | changed = true; 325 | this.dispose(); 326 | }//GEN-LAST:event_btnOKActionPerformed 327 | 328 | private void cmbRxDeviceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cmbRxDeviceActionPerformed 329 | switch (cmbRxDevice.getSelectedIndex()) { 330 | case 0: 331 | default: 332 | txtLO.setEnabled(true); 333 | radFirst.setEnabled(true); 334 | radSecond.setEnabled(true); 335 | radThird.setEnabled(true); 336 | radFirst.setSelected(true); 337 | break; 338 | case 1: 339 | txtLO.setEnabled(false); 340 | radFirst.setEnabled(false); 341 | radSecond.setEnabled(true); 342 | radThird.setEnabled(true); 343 | radSecond.setSelected(true); 344 | break; 345 | case 2: 346 | txtLO.setEnabled(true); 347 | radFirst.setEnabled(false); 348 | radSecond.setEnabled(true); 349 | radThird.setEnabled(true); 350 | radSecond.setSelected(true); 351 | break; 352 | case 3: 353 | txtLO.setEnabled(true); 354 | radFirst.setEnabled(false); 355 | radSecond.setEnabled(false); 356 | radThird.setEnabled(false); 357 | radFourth.setSelected(true); 358 | break; 359 | } 360 | }//GEN-LAST:event_cmbRxDeviceActionPerformed 361 | 362 | private void cmbRxDeviceMouseWheelMoved(java.awt.event.MouseWheelEvent evt) {//GEN-FIRST:event_cmbRxDeviceMouseWheelMoved 363 | var s = new Shared(); 364 | s.mouseWheelComboBoxHandler(evt.getWheelRotation(), cmbRxDevice); 365 | }//GEN-LAST:event_cmbRxDeviceMouseWheelMoved 366 | 367 | private void chkShowRealFreqActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkShowRealFreqActionPerformed 368 | // This setting isn't compatible with "apply to custom frequencies" 369 | if (chkShowRealFreq.isSelected()) { 370 | if (chkApplyToCustom.isSelected()) chkApplyToCustom.doClick(); 371 | chkApplyToCustom.setEnabled(false); 372 | } 373 | else { 374 | chkApplyToCustom.setEnabled(true); 375 | } 376 | }//GEN-LAST:event_chkShowRealFreqActionPerformed 377 | 378 | private void chkApplyToCustomActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkApplyToCustomActionPerformed 379 | // This setting isn't compatible with "Show IF" 380 | if (chkApplyToCustom.isSelected()) { 381 | if (chkShowRealFreq.isSelected()) chkShowRealFreq.doClick(); 382 | chkShowRealFreq.setEnabled(false); 383 | } 384 | else { 385 | chkShowRealFreq.setEnabled(true); 386 | } 387 | }//GEN-LAST:event_chkApplyToCustomActionPerformed 388 | 389 | // Variables declaration - do not modify//GEN-BEGIN:variables 390 | private javax.swing.JButton btnCancel; 391 | private javax.swing.JButton btnOK; 392 | private javax.swing.JPanel buttonPanel; 393 | private javax.swing.JCheckBox chkApplyToCustom; 394 | private javax.swing.JCheckBox chkShowRealFreq; 395 | private javax.swing.JComboBox cmbRxDevice; 396 | private javax.swing.ButtonGroup harmonicBG; 397 | private javax.swing.JPanel harmonicPanel; 398 | private javax.swing.JLabel lblHelpText; 399 | private javax.swing.JLabel lblLO; 400 | private javax.swing.JLabel lblRxDevice; 401 | private javax.swing.JRadioButton radFirst; 402 | private javax.swing.JRadioButton radFourth; 403 | private javax.swing.JRadioButton radSecond; 404 | private javax.swing.JRadioButton radThird; 405 | private javax.swing.JPanel satSettingsPanel; 406 | private javax.swing.JTextField txtLO; 407 | // End of variables declaration//GEN-END:variables 408 | } 409 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/Shared.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2023 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | package com.steeviebops.hacktvgui; 19 | 20 | /** 21 | * Various functions and methods not directly related to the GUI code. 22 | */ 23 | 24 | import java.awt.Desktop; 25 | import java.io.File; 26 | import java.io.FileInputStream; 27 | import java.io.FileOutputStream; 28 | import java.io.IOException; 29 | import java.io.InputStream; 30 | import java.io.Serializable; 31 | import java.net.URI; 32 | import java.net.URISyntaxException; 33 | import java.nio.charset.StandardCharsets; 34 | import java.nio.file.FileVisitResult; 35 | import java.nio.file.Files; 36 | import java.nio.file.Path; 37 | import java.nio.file.Paths; 38 | import java.nio.file.SimpleFileVisitor; 39 | import java.nio.file.StandardCopyOption; 40 | import java.nio.file.attribute.BasicFileAttributes; 41 | import java.security.CodeSource; 42 | import java.util.Date; 43 | import java.util.Enumeration; 44 | import java.util.Locale; 45 | import java.util.jar.JarEntry; 46 | import java.util.jar.JarFile; 47 | import java.util.zip.ZipEntry; 48 | import java.util.zip.ZipInputStream; 49 | import javax.swing.JComboBox; 50 | 51 | public class Shared implements Serializable { 52 | 53 | private static final long serialVersionUID = -8155770639405775482L; 54 | 55 | public int calculateLuhnCheckDigit(long input) { 56 | // Calculates a check digit for the specified input using the Luhn algorithm 57 | long t = 0; 58 | // Read backwards, doubling every other digit 59 | for (long l = input; l > 0; l = l / 100) { 60 | // Double l and add it to t. 61 | // If the result is greater than 9, the formula below will 62 | // add the individual digits, e.g. 14 is 1 + 4 = 5. 63 | t = t + ( ((l % 10 * 2) / 10) + ((l % 10 * 2) % 10) ); 64 | } 65 | // Read backwards again, add the remaining digits as-is 66 | for (long l = input / 10; l > 0; l = l / 100) { 67 | t = t + (l % 10); 68 | } 69 | // Multiply t by 9, the result of Mod10 is the check digit 70 | return (int) ((t * 9) % 10); 71 | } 72 | 73 | public boolean luhnCheck(Long input) { 74 | // Feed the full number to this method and it will return true or 75 | // false based on whether the check digit is valid or not. 76 | return calculateLuhnCheckDigit(input / 10) == (input % 10); 77 | } 78 | 79 | public boolean isNumeric(String strNum) { 80 | if (strNum == null) { 81 | return false; 82 | } 83 | try { 84 | Double.valueOf(strNum); 85 | } 86 | catch (NumberFormatException nfe) { 87 | return false; 88 | } 89 | return true; 90 | } 91 | 92 | public boolean isHex(String strHex) { 93 | if (strHex == null) { 94 | return false; 95 | } 96 | try { 97 | Long.valueOf(strHex, 16); 98 | } 99 | catch (NumberFormatException nfe) { 100 | return false; 101 | } 102 | return true; 103 | } 104 | 105 | public int wildcardFind(String pathToScan, String startsWith, String endsWith) { 106 | // Returns the number of files found in a directory with the specified start and end strings 107 | // Case insensitive, feed it with lowercase filenames 108 | String fileToFilter; 109 | var folderToScan = new File(pathToScan); 110 | File[] listOfFiles = folderToScan.listFiles(); 111 | int c = 0; 112 | // If the specified directory does not exist, return 0 and stop 113 | if (!Files.exists(folderToScan.toPath())) return 0; 114 | for (File listOfFile : listOfFiles) { 115 | if (listOfFile.isFile()) { 116 | fileToFilter = listOfFile.getName(); 117 | // If a file is found, increment c by one 118 | if (fileToFilter.toLowerCase(Locale.ENGLISH).startsWith(startsWith) 119 | && fileToFilter.toLowerCase(Locale.ENGLISH).endsWith(endsWith)) { 120 | c = c + 1; 121 | } 122 | } 123 | } 124 | return c; 125 | } 126 | 127 | public static void deleteFSObject(Path pathToBeDeleted) throws IOException { 128 | // Deletes the path specified to this method 129 | Files.walkFileTree(pathToBeDeleted, 130 | new SimpleFileVisitor() { 131 | @Override 132 | public FileVisitResult postVisitDirectory( 133 | Path dir, IOException exc) throws IOException { 134 | Files.delete(dir); 135 | return FileVisitResult.CONTINUE; 136 | } 137 | 138 | @Override 139 | public FileVisitResult visitFile( 140 | Path file, BasicFileAttributes attrs) 141 | throws IOException { 142 | Files.delete(file); 143 | return FileVisitResult.CONTINUE; 144 | } 145 | }); 146 | } 147 | 148 | public void copyResource(String res, String dest, Class c) throws IOException { 149 | InputStream src = c.getResourceAsStream(res); 150 | Files.copy(src, Paths.get(dest), StandardCopyOption.REPLACE_EXISTING); 151 | } 152 | 153 | public String stripQuotes(String FilePath) { 154 | // Bug fix for cases where a path containing quotes is pasted into 155 | // the file open prompt. This causes Swing to prepend the current 156 | // directory to the path (with the intended file path including 157 | // quotes at the end). This can cause things to break badly; if 158 | // this path is saved to the preferences store it can prevent the 159 | // application from opening! So we check for it and strip the path. 160 | if (FilePath.contains("\\\"") ) { 161 | FilePath = FilePath.substring(FilePath.indexOf("\"")); 162 | } 163 | if (FilePath.startsWith("\"")) FilePath = FilePath.substring(1); 164 | if (FilePath.endsWith("\"")) FilePath = FilePath.substring(0, FilePath.length() -1); 165 | return FilePath; 166 | } 167 | 168 | public Date getLastUpdatedTime(String jarFilePath, String classFilePath) { 169 | try (var jar = new JarFile(jarFilePath)) { 170 | Enumeration enumEntries = jar.entries(); 171 | while (enumEntries.hasMoreElements()) { 172 | JarEntry file = (JarEntry) enumEntries.nextElement(); 173 | if (file.getName().equals(classFilePath.substring(1))) { 174 | long time=file.getTime(); 175 | return time==-1?null: new Date(time); 176 | } 177 | } 178 | } 179 | catch (IOException e) { 180 | return null; 181 | } 182 | return null; 183 | } 184 | 185 | public void download(String url, String fileName) throws IOException, URISyntaxException { 186 | var connection = new URI(url).toURL().openConnection(); 187 | connection.setUseCaches(false); 188 | try (InputStream in = connection.getInputStream()) { 189 | Files.copy(in, Paths.get(fileName)); 190 | } 191 | } 192 | 193 | public String downloadToString(String url) throws IOException, URISyntaxException { 194 | // Downloads a file directly to a string, bypassing the file system 195 | var connection = new URI(url).toURL().openConnection(); 196 | connection.setUseCaches(false); 197 | try (InputStream in = connection.getInputStream()) { 198 | return new String(in.readAllBytes(), StandardCharsets.UTF_8); 199 | } 200 | } 201 | 202 | // Unzip code courtesy of https://www.baeldung.com/java-compress-and-uncompress 203 | public void unzipFile(String fileZip, String destination) throws IOException { 204 | var destDir = new File(destination); 205 | var buffer = new byte[1024]; 206 | try (var zis = new ZipInputStream(new FileInputStream(fileZip))) { 207 | var zipEntry = zis.getNextEntry(); 208 | while (zipEntry != null) { 209 | var newFile = newFile(destDir, zipEntry); 210 | if (zipEntry.isDirectory()) { 211 | if (!newFile.isDirectory() && !newFile.mkdirs()) { 212 | throw new IOException("Failed to create directory " + newFile); 213 | } 214 | } else { 215 | // fix for Windows-created archives 216 | var parent = newFile.getParentFile(); 217 | if (!parent.isDirectory() && !parent.mkdirs()) { 218 | throw new IOException("Failed to create directory " + parent); 219 | } 220 | 221 | try ( // write file content 222 | var fos = new FileOutputStream(newFile)) { 223 | int len; 224 | while ((len = zis.read(buffer)) > 0) { 225 | fos.write(buffer, 0, len); 226 | } 227 | } 228 | // Reset timestamp to original 229 | if (!newFile.setLastModified(zipEntry.getTime())) { 230 | System.err.println("Failed to set timestamp."); 231 | } 232 | } 233 | zipEntry = zis.getNextEntry(); 234 | } 235 | zis.closeEntry(); 236 | } 237 | } 238 | 239 | public File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { 240 | var destFile = new File(destinationDir, zipEntry.getName()); 241 | 242 | String destDirPath = destinationDir.getCanonicalPath(); 243 | String destFilePath = destFile.getCanonicalPath(); 244 | 245 | if (!destFilePath.startsWith(destDirPath + File.separator)) { 246 | throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); 247 | } 248 | 249 | return destFile; 250 | } 251 | 252 | public String getCurrentDirectory() { 253 | try { 254 | // Get the current directory path 255 | CodeSource codeSource = GUI.class.getProtectionDomain().getCodeSource(); 256 | var jarFile = new File(codeSource.getLocation().toURI().getPath()); 257 | return jarFile.getParentFile().getPath(); 258 | } 259 | catch (URISyntaxException ex) { 260 | System.out.println(ex); 261 | return ""; 262 | } 263 | } 264 | 265 | public void launchBrowser(String u) throws IOException { 266 | // Try using the native Desktop class 267 | if ( (Desktop.isDesktopSupported()) && 268 | (Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) ) { 269 | Desktop.getDesktop().browse(URI.create(u)); 270 | } 271 | else { 272 | // Try using xdg-open 273 | var p = new ProcessBuilder("xdg-open", u); 274 | p.start(); 275 | } 276 | } 277 | 278 | public void mouseWheelComboBoxHandler(int evt, JComboBox jcb) { 279 | /* 280 | * evt contains the number of clicks from the mouse wheel 281 | * A single spin upwards reports -1 282 | * A aingle spin downwards reports 1 283 | * 284 | * jcb is the name of the JComboBox that you want to manipulate 285 | */ 286 | if (jcb.isEnabled()) { // Don't do anything if the combobox is disabled 287 | if (evt < 0) { 288 | int p = evt * -1; // negative * negative = positive 289 | if (jcb.getSelectedIndex() - p >= 0) jcb.setSelectedIndex(jcb.getSelectedIndex() - p); 290 | } 291 | else if (evt > 0) { 292 | if (evt + jcb.getSelectedIndex() < jcb.getItemCount()) jcb.setSelectedIndex(jcb.getSelectedIndex() + evt); 293 | } 294 | } 295 | } 296 | 297 | } 298 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/TestSettingsDialogue.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 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 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 |
352 | -------------------------------------------------------------------------------- /src/com/steeviebops/hacktvgui/TestSettingsDialogue.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2025 Stephen McGarry 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | */ 18 | package com.steeviebops.hacktvgui; 19 | 20 | import java.io.File; 21 | import java.nio.file.Files; 22 | import java.nio.file.Path; 23 | import javax.swing.JFileChooser; 24 | import javax.swing.JOptionPane; 25 | 26 | public class TestSettingsDialogue extends javax.swing.JDialog { 27 | 28 | private boolean changed; 29 | private final String hackTVDir; 30 | private final String oldDir; 31 | 32 | /** 33 | * This method is called from within the constructor to initialize the form. 34 | * WARNING: Do NOT modify this code. The content of this method is always 35 | * regenerated by the Form Editor. 36 | */ 37 | @SuppressWarnings("unchecked") 38 | // //GEN-BEGIN:initComponents 39 | private void initComponents() { 40 | 41 | clockBG = new javax.swing.ButtonGroup(); 42 | testFileChooser = new javax.swing.JFileChooser(); 43 | locationPanel = new javax.swing.JPanel(); 44 | lblLocationDescriptor = new javax.swing.JLabel(); 45 | txtTSDir = new javax.swing.JTextField(); 46 | btnTSDir = new javax.swing.JButton(); 47 | displaySettingsPanel = new javax.swing.JPanel(); 48 | clockPanel = new javax.swing.JPanel(); 49 | radClockOff = new javax.swing.JRadioButton(); 50 | radClockOn = new javax.swing.JRadioButton(); 51 | radDateOn = new javax.swing.JRadioButton(); 52 | lblPhilipsDescriptor = new javax.swing.JLabel(); 53 | textFieldsPanel = new javax.swing.JPanel(); 54 | lblText1 = new javax.swing.JLabel(); 55 | txtTop = new javax.swing.JTextField(); 56 | lblText2 = new javax.swing.JLabel(); 57 | txtBottom = new javax.swing.JTextField(); 58 | buttonPanel = new javax.swing.JPanel(); 59 | btnOK = new javax.swing.JButton(); 60 | btnCancel = new javax.swing.JButton(); 61 | 62 | testFileChooser.setFileSelectionMode(javax.swing.JFileChooser.DIRECTORIES_ONLY); 63 | 64 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 65 | setTitle("Test signal settings"); 66 | setName("TestSignalSettings"); // NOI18N 67 | setResizable(false); 68 | 69 | locationPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("File location")); 70 | 71 | lblLocationDescriptor.setText("Specify the directory containing the test signal files."); 72 | 73 | txtTSDir.addMouseListener(new ContextMenuListener()); 74 | txtTSDir.addKeyListener(new java.awt.event.KeyAdapter() { 75 | public void keyPressed(java.awt.event.KeyEvent evt) { 76 | txtTSDirKeyPressed(evt); 77 | } 78 | }); 79 | 80 | btnTSDir.setText("Browse..."); 81 | btnTSDir.addActionListener(new java.awt.event.ActionListener() { 82 | public void actionPerformed(java.awt.event.ActionEvent evt) { 83 | btnTSDirActionPerformed(evt); 84 | } 85 | }); 86 | 87 | javax.swing.GroupLayout locationPanelLayout = new javax.swing.GroupLayout(locationPanel); 88 | locationPanel.setLayout(locationPanelLayout); 89 | locationPanelLayout.setHorizontalGroup( 90 | locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 91 | .addGroup(locationPanelLayout.createSequentialGroup() 92 | .addContainerGap() 93 | .addGroup(locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 94 | .addGroup(locationPanelLayout.createSequentialGroup() 95 | .addComponent(txtTSDir) 96 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 97 | .addComponent(btnTSDir)) 98 | .addGroup(locationPanelLayout.createSequentialGroup() 99 | .addComponent(lblLocationDescriptor) 100 | .addGap(0, 0, Short.MAX_VALUE))) 101 | .addContainerGap()) 102 | ); 103 | locationPanelLayout.setVerticalGroup( 104 | locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 105 | .addGroup(locationPanelLayout.createSequentialGroup() 106 | .addComponent(lblLocationDescriptor) 107 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 108 | .addGroup(locationPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 109 | .addComponent(txtTSDir, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 110 | .addComponent(btnTSDir)) 111 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 112 | ); 113 | 114 | displaySettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Display settings")); 115 | 116 | clockPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Clock options")); 117 | 118 | clockBG.add(radClockOff); 119 | radClockOff.setText("Off"); 120 | 121 | clockBG.add(radClockOn); 122 | radClockOn.setText("Time"); 123 | radClockOn.setActionCommand("Time"); 124 | 125 | clockBG.add(radDateOn); 126 | radDateOn.setText("Time and date"); 127 | radDateOn.setActionCommand("Time and date"); 128 | 129 | javax.swing.GroupLayout clockPanelLayout = new javax.swing.GroupLayout(clockPanel); 130 | clockPanel.setLayout(clockPanelLayout); 131 | clockPanelLayout.setHorizontalGroup( 132 | clockPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 133 | .addGroup(clockPanelLayout.createSequentialGroup() 134 | .addContainerGap() 135 | .addGroup(clockPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 136 | .addComponent(radClockOff) 137 | .addComponent(radClockOn) 138 | .addComponent(radDateOn)) 139 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 140 | ); 141 | clockPanelLayout.setVerticalGroup( 142 | clockPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 143 | .addGroup(clockPanelLayout.createSequentialGroup() 144 | .addContainerGap() 145 | .addComponent(radClockOff) 146 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 147 | .addComponent(radClockOn) 148 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 149 | .addComponent(radDateOn) 150 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 151 | ); 152 | 153 | lblPhilipsDescriptor.setText("These settings apply only to the Philips and FuBK test patterns."); 154 | 155 | textFieldsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Text fields")); 156 | 157 | lblText1.setText("Top/Left"); 158 | 159 | txtTop.addMouseListener(new ContextMenuListener()); 160 | txtTop.addKeyListener(new java.awt.event.KeyAdapter() { 161 | public void keyPressed(java.awt.event.KeyEvent evt) { 162 | txtTopKeyPressed(evt); 163 | } 164 | public void keyTyped(java.awt.event.KeyEvent evt) { 165 | txtTopKeyTyped(evt); 166 | } 167 | }); 168 | 169 | lblText2.setText("Bottom/Right"); 170 | 171 | txtBottom.addMouseListener(new ContextMenuListener()); 172 | txtBottom.addKeyListener(new java.awt.event.KeyAdapter() { 173 | public void keyPressed(java.awt.event.KeyEvent evt) { 174 | txtBottomKeyPressed(evt); 175 | } 176 | public void keyTyped(java.awt.event.KeyEvent evt) { 177 | txtBottomKeyTyped(evt); 178 | } 179 | }); 180 | 181 | javax.swing.GroupLayout textFieldsPanelLayout = new javax.swing.GroupLayout(textFieldsPanel); 182 | textFieldsPanel.setLayout(textFieldsPanelLayout); 183 | textFieldsPanelLayout.setHorizontalGroup( 184 | textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 185 | .addGroup(textFieldsPanelLayout.createSequentialGroup() 186 | .addContainerGap() 187 | .addGroup(textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 188 | .addComponent(lblText2) 189 | .addComponent(lblText1)) 190 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 191 | .addGroup(textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 192 | .addComponent(txtTop) 193 | .addComponent(txtBottom)) 194 | .addContainerGap()) 195 | ); 196 | textFieldsPanelLayout.setVerticalGroup( 197 | textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 198 | .addGroup(textFieldsPanelLayout.createSequentialGroup() 199 | .addContainerGap() 200 | .addGroup(textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 201 | .addComponent(lblText1) 202 | .addComponent(txtTop, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 203 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 204 | .addGroup(textFieldsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 205 | .addComponent(lblText2) 206 | .addComponent(txtBottom, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 207 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 208 | ); 209 | 210 | javax.swing.GroupLayout displaySettingsPanelLayout = new javax.swing.GroupLayout(displaySettingsPanel); 211 | displaySettingsPanel.setLayout(displaySettingsPanelLayout); 212 | displaySettingsPanelLayout.setHorizontalGroup( 213 | displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 214 | .addGroup(displaySettingsPanelLayout.createSequentialGroup() 215 | .addContainerGap() 216 | .addGroup(displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 217 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, displaySettingsPanelLayout.createSequentialGroup() 218 | .addComponent(textFieldsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 219 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 220 | .addComponent(clockPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 221 | .addGap(14, 14, 14)) 222 | .addGroup(displaySettingsPanelLayout.createSequentialGroup() 223 | .addComponent(lblPhilipsDescriptor) 224 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) 225 | ); 226 | displaySettingsPanelLayout.setVerticalGroup( 227 | displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 228 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, displaySettingsPanelLayout.createSequentialGroup() 229 | .addGap(0, 0, 0) 230 | .addComponent(lblPhilipsDescriptor) 231 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 232 | .addGroup(displaySettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) 233 | .addComponent(clockPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 234 | .addComponent(textFieldsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 235 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 236 | ); 237 | 238 | btnOK.setText("OK"); 239 | btnOK.addActionListener(new java.awt.event.ActionListener() { 240 | public void actionPerformed(java.awt.event.ActionEvent evt) { 241 | btnOKActionPerformed(evt); 242 | } 243 | }); 244 | buttonPanel.add(btnOK); 245 | 246 | btnCancel.setText("Cancel"); 247 | btnCancel.addActionListener(new java.awt.event.ActionListener() { 248 | public void actionPerformed(java.awt.event.ActionEvent evt) { 249 | btnCancelActionPerformed(evt); 250 | } 251 | }); 252 | buttonPanel.add(btnCancel); 253 | 254 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 255 | getContentPane().setLayout(layout); 256 | layout.setHorizontalGroup( 257 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 258 | .addGroup(layout.createSequentialGroup() 259 | .addContainerGap() 260 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 261 | .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 262 | .addGroup(layout.createSequentialGroup() 263 | .addComponent(displaySettingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 264 | .addGap(0, 0, Short.MAX_VALUE)) 265 | .addComponent(locationPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 266 | .addContainerGap()) 267 | ); 268 | layout.setVerticalGroup( 269 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 270 | .addGroup(layout.createSequentialGroup() 271 | .addContainerGap() 272 | .addComponent(locationPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 273 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 274 | .addComponent(displaySettingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 275 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 276 | .addComponent(buttonPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) 277 | .addContainerGap()) 278 | ); 279 | 280 | pack(); 281 | }// //GEN-END:initComponents 282 | 283 | public TestSettingsDialogue(java.awt.Frame parent, boolean modal, String hackTVDirPath) { 284 | super(parent, modal); 285 | initComponents(); 286 | super.setLocationRelativeTo(parent); 287 | hackTVDir = hackTVDirPath; 288 | oldDir = GUI.PREFS.get("testdir", ""); 289 | txtTSDir.setText(oldDir); 290 | txtTop.setText(GUI.PREFS.get("philipstext1", "")); 291 | txtBottom.setText(GUI.PREFS.get("philipstext2", "")); 292 | switch (GUI.PREFS.getInt("philipsclock", 0)) { 293 | case 0: 294 | default: 295 | radClockOff.setSelected(true); 296 | break; 297 | case 1: 298 | radClockOn.setSelected(true); 299 | break; 300 | case 2: 301 | radDateOn.setSelected(true); 302 | break; 303 | } 304 | btnCancel.requestFocus(); 305 | } 306 | 307 | public boolean settingsChanged() { 308 | // This boolean instructs the main thread to refresh the test signal list 309 | return changed; 310 | } 311 | 312 | private void processTypedKey(int kc) { 313 | switch(kc) { 314 | case 10: 315 | // Enter key 316 | btnOK.doClick(); 317 | break; 318 | case 27: 319 | // Esc key 320 | btnCancel.doClick(); 321 | break; 322 | default: 323 | // Do nothing for other keys 324 | break; 325 | } 326 | } 327 | 328 | private void btnOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOKActionPerformed 329 | // If the directory has changed, process the new one 330 | if (!txtTSDir.getText().equals(oldDir)) { 331 | var s = new Shared(); 332 | String l = s.stripQuotes(txtTSDir.getText()); 333 | // Make sure that the selected directory contains what we need 334 | if (Files.exists(Path.of(l + File.separator + "pm8546g.bin"))) { 335 | // Save the chosen directory to prefs 336 | GUI.PREFS.put("testdir", l); 337 | changed = true; 338 | } 339 | else if (txtTSDir.getText().isBlank()) { 340 | // No directory specified, default to the hacktv location 341 | GUI.PREFS.remove("testdir"); 342 | changed = true; 343 | } 344 | else { 345 | JOptionPane.showMessageDialog( 346 | null, 347 | "The pm8546g.bin file was not be found in the specified location.", 348 | GUI.APP_NAME, 349 | JOptionPane.WARNING_MESSAGE 350 | ); 351 | return; 352 | } 353 | } 354 | else { 355 | changed = false; 356 | } 357 | // Save pattern preferences 358 | /* 359 | The maximum character lengths for each pattern is as follows: 360 | 361 | Philips 4:3 Top field: 9 (8 for NTSC), bottom field: 13 362 | Philips 16:9 Top field: 6, bottom field: 10 363 | FuBK 4:3 8 characters in each field 364 | FuBK 16:9 6 characters in each field 365 | 366 | */ 367 | int t = txtTop.getText().length(); 368 | int b = txtBottom.getText().length(); 369 | if (txtTop.getText().isBlank()) { 370 | GUI.PREFS.remove("philipstext1"); 371 | } 372 | else { 373 | // Truncate if longer than 9 characters 374 | if (t > 9) txtTop.setText(txtTop.getText().substring(0,9)); 375 | GUI.PREFS.put("philipstext1", txtTop.getText()); 376 | } 377 | if (txtBottom.getText().isBlank()) { 378 | GUI.PREFS.remove("philipstext2"); 379 | } 380 | else { 381 | // Truncate if longer than 13 characters 382 | if (b > 13) txtBottom.setText(txtBottom.getText().substring(0,13)); 383 | GUI.PREFS.put("philipstext2", txtBottom.getText()); 384 | } 385 | if (radClockOff.isSelected()) { 386 | GUI.PREFS.remove("philipsclock"); 387 | } 388 | else if (radClockOn.isSelected()) { 389 | GUI.PREFS.putInt("philipsclock", 1); 390 | } 391 | else if (radDateOn.isSelected()) { 392 | GUI.PREFS.putInt("philipsclock", 2); 393 | } 394 | this.dispose(); 395 | }//GEN-LAST:event_btnOKActionPerformed 396 | 397 | private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed 398 | this.dispose(); 399 | }//GEN-LAST:event_btnCancelActionPerformed 400 | 401 | private void btnTSDirActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnTSDirActionPerformed 402 | testFileChooser.setCurrentDirectory( 403 | new File(GUI.PREFS.get("testdir", hackTVDir)) 404 | ); 405 | int result = testFileChooser.showOpenDialog(this); 406 | if (result == JFileChooser.APPROVE_OPTION) { 407 | String d = testFileChooser.getSelectedFile().toString(); 408 | txtTSDir.setText(d); 409 | } 410 | }//GEN-LAST:event_btnTSDirActionPerformed 411 | 412 | private void txtTopKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtTopKeyTyped 413 | if (txtTop.getText().length() >= 9) { 414 | evt.consume(); 415 | } 416 | }//GEN-LAST:event_txtTopKeyTyped 417 | 418 | private void txtBottomKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtBottomKeyTyped 419 | if (txtBottom.getText().length() >= 13) { 420 | evt.consume(); 421 | } 422 | }//GEN-LAST:event_txtBottomKeyTyped 423 | 424 | private void txtTSDirKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtTSDirKeyPressed 425 | processTypedKey(evt.getKeyCode()); 426 | }//GEN-LAST:event_txtTSDirKeyPressed 427 | 428 | private void txtTopKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtTopKeyPressed 429 | processTypedKey(evt.getKeyCode()); 430 | }//GEN-LAST:event_txtTopKeyPressed 431 | 432 | private void txtBottomKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtBottomKeyPressed 433 | processTypedKey(evt.getKeyCode()); 434 | }//GEN-LAST:event_txtBottomKeyPressed 435 | 436 | // Variables declaration - do not modify//GEN-BEGIN:variables 437 | private javax.swing.JButton btnCancel; 438 | private javax.swing.JButton btnOK; 439 | private javax.swing.JButton btnTSDir; 440 | private javax.swing.JPanel buttonPanel; 441 | private javax.swing.ButtonGroup clockBG; 442 | private javax.swing.JPanel clockPanel; 443 | private javax.swing.JPanel displaySettingsPanel; 444 | private javax.swing.JLabel lblLocationDescriptor; 445 | private javax.swing.JLabel lblPhilipsDescriptor; 446 | private javax.swing.JLabel lblText1; 447 | private javax.swing.JLabel lblText2; 448 | private javax.swing.JPanel locationPanel; 449 | private javax.swing.JRadioButton radClockOff; 450 | private javax.swing.JRadioButton radClockOn; 451 | private javax.swing.JRadioButton radDateOn; 452 | private javax.swing.JFileChooser testFileChooser; 453 | private javax.swing.JPanel textFieldsPanel; 454 | private javax.swing.JTextField txtBottom; 455 | private javax.swing.JTextField txtTSDir; 456 | private javax.swing.JTextField txtTop; 457 | // End of variables declaration//GEN-END:variables 458 | } 459 | -------------------------------------------------------------------------------- /src/com/steeviebops/resources/bandplans.ini: -------------------------------------------------------------------------------- 1 | ; Band plan file 2 | ; All characters (including spaces) are allowed in channel numbers/names except the backslash, square brackets and semi-colon (which is used for comments) 3 | 4 | [bandplans.ini] 5 | Version=1.00 6 | 7 | [uhf_eu] 8 | region=Western Europe 9 | E21=471250000 10 | E22=479250000 11 | E23=487250000 12 | E24=495250000 13 | E25=503250000 14 | E26=511250000 15 | E27=519250000 16 | E28=527250000 17 | E29=535250000 18 | E30=543250000 19 | E31=551250000 20 | E32=559250000 21 | E33=567250000 22 | E34=575250000 23 | E35=583250000 24 | E36=591250000 25 | E37=599250000 26 | E38=607250000 27 | E39=615250000 28 | E40=623250000 29 | E41=631250000 30 | E42=639250000 31 | E43=647250000 32 | E44=655250000 33 | E45=663250000 34 | E46=671250000 35 | E47=679250000 36 | E48=687250000 37 | ; E49 to E60 were deallocated in 2020, pending allocation to rural 5G mobile services. 38 | E49=695250000 39 | E50=703250000 40 | E51=711250000 41 | E52=719250000 42 | E53=727250000 43 | E54=735250000 44 | E55=743250000 45 | E56=751250000 46 | E57=759250000 47 | E58=767250000 48 | E59=775250000 49 | E60=783250000 50 | ; E61 to E69 were deallocated after analogue switch-off in 2012. Now used for LTE/4G mobile. 51 | E61=791250000 52 | E62=799250000 53 | E63=807250000 54 | E64=815250000 55 | E65=823250000 56 | E66=831250000 57 | E67=839250000 58 | E68=847250000 59 | E69=855250000 60 | 61 | [vhf_eu] 62 | region=Continental Europe 63 | E2=48250000 64 | E3=55250000 65 | E4=62250000 66 | E5=175250000 67 | E6=182250000 68 | E7=189250000 69 | E8=196250000 70 | E9=203250000 71 | E10=210250000 72 | E11=217250000 73 | E12=224250000 74 | 75 | [vhf_ie] 76 | region=Ireland 77 | ; Channel A was never used in service 78 | A=45750000 79 | B=53750000 80 | C=61750000 81 | D=175250000 82 | E=183250000 83 | F=191250000 84 | G=199250000 85 | H=207250000 86 | I=215250000 87 | J=223250000 88 | 89 | [vhf_ntsc] 90 | region=Americas (NTSC) 91 | A2=55250000 92 | A3=61250000 93 | A4=67250000 94 | A5=77250000 95 | A6=83250000 96 | A7=175250000 97 | A8=181250000 98 | A9=187250000 99 | A10=193250000 100 | A11=199250000 101 | A12=205250000 102 | A13=211250000 103 | 104 | [uhf_ntsc] 105 | region=Americas (NTSC) 106 | A14=471250000 107 | A15=477250000 108 | A16=483250000 109 | A17=489250000 110 | A18=495250000 111 | A19=501250000 112 | A20=507250000 113 | A21=513250000 114 | A22=519250000 115 | A23=525250000 116 | A24=531250000 117 | A25=537250000 118 | A26=543250000 119 | A27=549250000 120 | A28=555250000 121 | A29=561250000 122 | A30=567250000 123 | A31=573250000 124 | A32=579250000 125 | A33=585250000 126 | A34=591250000 127 | A35=597250000 128 | A36=603250000 129 | ; A37 is used for radio astronomy in the US and was not available for TV use 130 | ; A38 to A51 were deallocated by the FCC in 2020 following a spectrum repack 131 | A37=609250000 132 | A38=615250000 133 | A39=621250000 134 | A40=627250000 135 | A41=633250000 136 | A42=639250000 137 | A43=645250000 138 | A44=651250000 139 | A45=657250000 140 | A46=663250000 141 | A47=669250000 142 | A48=675250000 143 | A49=681250000 144 | A50=687250000 145 | A51=693250000 146 | ; A52 to A69 were deallocated by the FCC in 2009 to be used by LTE/4G cellular services 147 | A52=699250000 148 | A53=705250000 149 | A54=711250000 150 | A55=717250000 151 | A56=723250000 152 | A57=729250000 153 | A58=735250000 154 | A59=741250000 155 | A60=747250000 156 | A61=753250000 157 | A62=759250000 158 | A63=765250000 159 | A64=771250000 160 | A65=777250000 161 | A66=783250000 162 | A67=789250000 163 | A68=795250000 164 | A69=801250000 165 | ; A70 to A83 were deallocated by the FCC in 1983 to be used by AMPS analog cellular services 166 | A70=807250000 167 | A71=813250000 168 | A72=819250000 169 | A73=825250000 170 | A74=831250000 171 | A75=837250000 172 | A76=843250000 173 | A77=849250000 174 | A78=855250000 175 | A79=861250000 176 | A80=867250000 177 | A81=873250000 178 | A82=879250000 179 | A83=885250000 180 | 181 | [vhf_fr_l] 182 | region=France (SECAM) 183 | L2=55750000 184 | L3=60500000 185 | L4=63750000 186 | L5=176000000 187 | L6=184000000 188 | L7=192000000 189 | L8=200000000 190 | L9=208000000 191 | L10=216000000 192 | 193 | [vhf_ru] 194 | region=Russia 195 | R1=49750000 196 | R2=59250000 197 | R3=77250000 198 | ; Channels 4 and 5 overlap with the standard VHF-FM band 199 | R4=85250000 200 | R5=93250000 201 | R6=175250000 202 | R7=183250000 203 | R8=191250000 204 | R9=199250000 205 | R10=207250000 206 | R11=215250000 207 | R12=223250000 208 | 209 | [vhf_gb] 210 | region=United Kingdom (405 line) 211 | B1=45000000 212 | B2=51750000 213 | B3=56750000 214 | B4=61750000 215 | B5=66750000 216 | B6=179750000 217 | B7=184750000 218 | B8=189750000 219 | B9=194750000 220 | B10=199750000 221 | B11=204750000 222 | B12=209750000 223 | B13=214750000 224 | ; B14 was allocated but never used and some TVs didn't support it 225 | B14=219750000 226 | 227 | [vhf_fr_e] 228 | region=France (819 line) 229 | F2=52400000 230 | F4=65550000 231 | F5=164000000 232 | F6=173400000 233 | F7=177150000 234 | F8a=185250000 235 | F8=186550000 236 | F9=190300000 237 | F10=199700000 238 | F11=203450000 239 | F12=212850000 240 | 241 | [uhf_cn] 242 | region=China 243 | C13=471250000 244 | C14=479250000 245 | C15=487250000 246 | C16=495250000 247 | C17=503250000 248 | C18=511250000 249 | C19=519250000 250 | C20=527250000 251 | C21=535250000 252 | C22=543250000 253 | C23=551250000 254 | C24=559250000 255 | C25=607250000 256 | C26=615250000 257 | C27=623250000 258 | C28=631250000 259 | C29=639250000 260 | C30=647250000 261 | C31=655250000 262 | C32=663250000 263 | C33=671250000 264 | C34=679250000 265 | C35=687250000 266 | C36=695250000 267 | ; Channels 37 to 49 were deallocated in 2020. Now used for 5G mobile services. 268 | C37=703250000 269 | C38=711250000 270 | C39=719250000 271 | C40=727250000 272 | C41=735250000 273 | C42=743250000 274 | C43=751250000 275 | C44=759250000 276 | C45=767250000 277 | C46=775250000 278 | C47=783250000 279 | C48=791250000 280 | C49=799250000 281 | ; Channels 50 to 57 were deallocated in 1995 to be used by trunked radio services. 282 | C50=807250000 283 | C51=815250000 284 | C52=823250000 285 | C53=831250000 286 | C54=839250000 287 | C55=847250000 288 | C56=855250000 289 | C57=863250000 290 | ; Channels 58 to 68 (870MHz to 958MHz) were allocated but never used and most TVs didn't support it. 291 | 292 | [vhf_cn] 293 | region=China 294 | C1=49750000 295 | C2=57750000 296 | C3=65750000 297 | C4=77250000 298 | ; Channel 5 overlaps with the standard VHF-FM band 299 | C5=85250000 300 | C6=168250000 301 | C7=176250000 302 | C8=184250000 303 | C9=192250000 304 | C10=200250000 305 | C11=208250000 306 | C12=216250000 307 | 308 | [uhf_au] 309 | region=Australia 310 | H28=527250000 311 | H29=534250000 312 | H30=541250000 313 | H31=548250000 314 | H32=555250000 315 | H33=562250000 316 | H34=569250000 317 | H35=576250000 318 | H36=583250000 319 | H37=590250000 320 | H38=597250000 321 | H39=604250000 322 | H40=611250000 323 | H41=618250000 324 | H42=625250000 325 | H43=632250000 326 | H44=639250000 327 | H45=646250000 328 | H46=653250000 329 | H47=660250000 330 | H48=667250000 331 | H49=674250000 332 | H50=681250000 333 | H51=688250000 334 | H52=695250000 335 | H53=702250000 336 | H54=709250000 337 | H55=716250000 338 | H56=723250000 339 | H57=730250000 340 | H58=737250000 341 | H59=744250000 342 | H60=751250000 343 | H61=758250000 344 | H62=765250000 345 | H63=772250000 346 | H64=779250000 347 | H65=786250000 348 | H66=793250000 349 | H67=800250000 350 | H68=807250000 351 | H69=814250000 352 | 353 | [vhf_au] 354 | region=Australia 355 | H0=46250000 356 | H1=57250000 357 | H2=64250000 358 | H3=86250000 359 | H4=95250000 360 | H5=102250000 361 | H5A=138250000 362 | H6=175250000 363 | H7=182250000 364 | H8=189250000 365 | H9=196250000 366 | H9A=203250000 367 | H10=210250000 368 | H11=217250000 369 | H12=224250000 370 | 371 | [uhf_nz] 372 | region=New Zealand 373 | 25=503250000 374 | 26=511250000 375 | 27=519250000 376 | 28=527250000 377 | 29=535250000 378 | 30=543250000 379 | 31=551250000 380 | 32=559250000 381 | 33=567250000 382 | 34=575250000 383 | 35=583250000 384 | 36=591250000 385 | 37=599250000 386 | 38=607250000 387 | 39=615250000 388 | 40=623250000 389 | 41=631250000 390 | 42=639250000 391 | 43=647250000 392 | 44=655250000 393 | 45=663250000 394 | 46=671250000 395 | 47=679250000 396 | 48=687250000 397 | 49=695250000 398 | 50=703250000 399 | 51=711250000 400 | 52=719250000 401 | 53=727250000 402 | 54=735250000 403 | 55=743250000 404 | 56=751250000 405 | 57=759250000 406 | 58=767250000 407 | 59=775250000 408 | 60=783250000 409 | 61=791250000 410 | 62=799250000 411 | 412 | [vhf_nz] 413 | region=New Zealand 414 | 0=45250000 415 | 1=55250000 416 | 2=62250000 417 | 6=175250000 418 | 7=182250000 419 | 8=189250000 420 | 9=196250000 421 | 9A=203250000 422 | 10=210250000 423 | 11=217250000 424 | 12=224250000 425 | 426 | [uhf_jp] 427 | ; Japanese UHF frequencies are identical to North American channels A14-A63 428 | ; but the channel numbers are one lower. 429 | region=Japan 430 | J13=471250000 431 | J14=477250000 432 | J15=483250000 433 | J16=489250000 434 | J17=495250000 435 | J18=501250000 436 | J19=507250000 437 | J20=513250000 438 | J21=519250000 439 | J22=525250000 440 | J23=531250000 441 | J24=537250000 442 | J25=543250000 443 | J26=549250000 444 | J27=555250000 445 | J28=561250000 446 | J29=567250000 447 | J30=573250000 448 | J31=579250000 449 | J32=585250000 450 | J33=591250000 451 | J34=597250000 452 | J35=603250000 453 | J36=609250000 454 | J37=615250000 455 | J38=621250000 456 | J39=627250000 457 | J40=633250000 458 | J41=639250000 459 | J42=645250000 460 | J43=651250000 461 | J44=657250000 462 | J45=663250000 463 | J46=669250000 464 | J47=675250000 465 | J48=681250000 466 | J49=687250000 467 | J50=693250000 468 | J51=699250000 469 | J52=705250000 470 | J53=711250000 471 | J54=717250000 472 | J55=723250000 473 | J56=729250000 474 | J57=735250000 475 | J58=741250000 476 | J59=747250000 477 | J60=753250000 478 | J61=759250000 479 | J62=765250000 480 | 481 | [vhf_jp] 482 | region=Japan 483 | ; Channels J1-J3 overlap the standard VHF-FM band (Japan uses 76-90 MHz for FM radio). 484 | J1=91250000 485 | J2=97250000 486 | J3=103250000 487 | J4=171250000 488 | J5=177250000 489 | J6=183250000 490 | J7=189250000 491 | J8=193250000 492 | J9=199250000 493 | J10=205250000 494 | J11=211250000 495 | J12=217250000 496 | 497 | [vhf_za] 498 | region=South Africa 499 | 1=43250000 500 | 2=52250000 501 | 3=60250000 502 | 4=175250000 503 | 5=183250000 504 | 6=191250000 505 | 7=199250000 506 | 8=207250000 507 | 9=215250000 508 | 10=223250000 509 | 11=231250000 510 | 12=239250000 511 | 13=247250000 512 | 513 | [bsb] 514 | ; Based on information provided by fsphil at https://www.sanslogic.co.uk/dmac/bsb.html 515 | region=BSB 516 | chid=bsb_id 517 | lo=10769180000 ; Standard LO of BSB Squarials/LNBs 518 | 1 (Movie Ch)=12091900000 ; Transponder 20 519 | 2 (Sports Ch)=11938460000 ; Transponder 12 520 | 3 (Galaxy)=11861740000 ; Transponder 8 521 | 4 (Power St)=12015180000 ; Transponder 16 522 | 5 (Now)=11785020000 ; Transponder 4 523 | ; Irish (ROI) DBS channels are listed below 524 | ; These were never used but are available for use on BSB receivers 525 | 6=11746660000 ; Transponder 2 526 | 7=11823380000 ; Transponder 6 527 | 8=11900100000 ; Transponder 10 528 | 9=11976820000 ; Transponder 14 529 | 10=12053540000 ; Transponder 18 530 | 531 | [bsb_id] 532 | ; MAC channel IDs for BSB receivers. The channel name/number must be identical to the IF section. 533 | 5 (Now)=20B1 534 | 3 (Galaxy)=70B2 535 | 2 (Sports Ch)=48B3 536 | 4 (Power St)=D0B4 537 | 1 (Movie Ch)=E8B5 538 | 539 | [astra] 540 | region=Astra 19.2°E 541 | ; Band A 542 | 1=11214250000 543 | 2=11229000000 544 | 3=11243750000 545 | 4=11258500000 546 | 5=11273250000 547 | 6=11288000000 548 | 7=11302750000 549 | 8=11317500000 550 | 9=11332250000 551 | 10=11347000000 552 | 11=11361750000 553 | 12=11376500000 554 | 13=11391250000 555 | 14=11406000000 556 | 15=11420750000 557 | 16=11435500000 558 | ; Band B 559 | 17=11464250000 560 | 18=11479000000 561 | 19=11493750000 562 | 20=11508500000 563 | 21=11523250000 564 | 22=11538000000 565 | 23=11552750000 566 | 24=11567500000 567 | 25=11582250000 568 | 26=11597000000 569 | 27=11611750000 570 | 28=11626500000 571 | 29=11641250000 572 | 30=11656000000 573 | 31=11670750000 574 | 32=11685500000 575 | ; Band C 576 | 33=10964250000 577 | 34=10979000000 578 | 35=10993750000 579 | 36=11008500000 580 | 37=11023250000 581 | 38=11038000000 582 | 39=11052750000 583 | 40=11067500000 584 | 41=11082250000 585 | 42=11097000000 586 | 43=11111750000 587 | 44=11126500000 588 | 45=11141250000 589 | 46=11156000000 590 | 47=11170750000 591 | 48=11185500000 592 | ; Band D 593 | 49=10714250000 594 | 50=10729000000 595 | 51=10743750000 596 | 52=10758500000 597 | 53=10773250000 598 | 54=10788000000 599 | 55=10802750000 600 | 56=10817500000 601 | 57=10832250000 602 | 58=10847000000 603 | 59=10861750000 604 | 60=10876500000 605 | 61=10891250000 606 | 62=10906000000 607 | 63=10920750000 608 | 64=10935500000 609 | -------------------------------------------------------------------------------- /src/com/steeviebops/resources/captainjack.ini: -------------------------------------------------------------------------------- 1 | [Modes.ini] 2 | FileVersion=5.4c 3 | 4 | [videomodes] 5 | pal=i, g, pal-d, pal-fm, pal-n, pal-m, pal60-i, pal, pal60, 525pal 6 | ntsc=m, ntsc-fm, ntsc-bs, ntsc-i, ntsc-a, apollo-fsc-fm, m-cbs405, ntsc, ntsc-405, cbs405 7 | secam=l, d, secam-b, secam-fm, secam-i, secam 8 | other=a, e, 240-am, 30-am, apollo-fm, nbtv-am, 405-i, 405, 819, 240, 30, apollo, nbtv 9 | mac=d2mac-fm, dmac-fm, d2mac-am, dmac-am, d2mac, dmac 10 | 11 | [videocrypt] 12 | free=Free access/soft scrambled (no card required) 13 | sky12=Conditional access (Sky 12 card) 14 | sky11=Conditional access (Sky 11 card) 15 | sky10=Conditional access (Sky 10 card) 16 | sky10ppv=Pay-per-view mode (Sky 10 card) 17 | sky09=Conditional access (Sky 09 card) 18 | sky09nano=Conditional access (Sky 09 card with nanos) 19 | sky07=Conditional access (Sky 07 card) 20 | sky06=Conditional access (Sky 06 card) 21 | sky05=Conditional access (Sky 05 card) 22 | sky04=Conditional access (Sky 04 card) 23 | sky03=Conditional access (Sky 03 card) 24 | sky02=Conditional access (Sky 02 card) 25 | tac1=Conditional access (Old Adult Channel card) 26 | tac2=Conditional access (Newer Adult Channel card) 27 | jstv=Conditional access (JSTV card) 28 | skynz02=Conditional access (Sky New Zealand 02 card) 29 | skynz01=Conditional access (Sky New Zealand 01 card) 30 | dmx=Conditional access (DMX card) 31 | scast=Conditional access (Sportscast card) 32 | xtea=Conditional access (xtea mode) 33 | ppv=Pay-per-view mode (e.g. phone cards) 34 | 35 | [videocrypt2] 36 | free=Free access/soft scrambled (no card required) 37 | conditional=Conditional access (MultiChoice card) 38 | 39 | [videocrypts] 40 | free=Free access/soft scrambled (no card required) 41 | conditional=Conditional access (BBC Select card) 42 | 43 | [syster] 44 | premiere-fa=Free access (Premiere Germany) 45 | premiere-ca=Conditional access (Premiere Germany) 46 | cfrfa=Free access (Canal+ France) 47 | cfrca=Conditional access (Canal+ France) 48 | cplfa=Free access (Canal+ Poland) 49 | cesfa=Free access (Canal+ Spain) 50 | ntvfa=Free access (HTB+ Russia) 51 | chorfa=Free access (Canal+ Horizons) 52 | 53 | [eurocrypt] 54 | ; If no parameter is to be specified on the command line, enter the word "blank" (without the quotation marks) 55 | ; This is treated as a placeholder and will be removed automatically 56 | blank=No conditional access (free) 57 | bbcprime=EuroCrypt M (BBC Prime) 58 | cplusfr=EuroCrypt M (Canal+ France 4:3) 59 | cplusfr169=EuroCrypt M (Canal+ France 16:9) 60 | ctv=EuroCrypt M (CTV) 61 | filmnet=EuroCrypt M (FilmNet) 62 | multivisio=EuroCrypt M (Multivisio) 63 | tv3=EuroCrypt M (TV3) 64 | tv1000=EuroCrypt M (TV 1000) 65 | tvcable=EuroCrypt M (TV Cable) 66 | tvplus=EuroCrypt M (TVPlus) 67 | visiopass=EuroCrypt M (Visiopass) 68 | teletv=EuroCrypt S (Tele-TV) 69 | ctvs=EuroCrypt S2 (CTV) 70 | eros=EuroCrypt S2 (Eros) 71 | nrk=EuroCrypt S2 (NRK) 72 | rdv=EuroCrypt S2 (RDV) 73 | tv2=EuroCrypt S2 (TV2 Norway) 74 | tvs=EuroCrypt S2 (TV-S) 75 | cplus=EuroCrypt 3DES (Canal+ Denmark) 76 | 77 | [logos] 78 | hacktv=hacktv 79 | cartoonnetwork=Cartoon Network 80 | tv1000=TV1000 81 | filmnet1=FilmNet1 82 | canal+=Canal+ 83 | eurotica=Eurotica 84 | mtv=MTV 85 | tac=The Adult Channel 86 | filmnet=FilmNet 87 | multichoice=MultiChoice 88 | skyone=Sky One 89 | sky=Sky 90 | 91 | [testcards] 92 | colourbars=Colour bars 93 | pm5544=Philips 4:3 94 | pm5644=Philips 16:9 95 | ueitm=UEIT (Soviet) 96 | fubk=FuBK 97 | 98 | ; PAL 99 | [i] 100 | name=PAL-I (625 lines, 25 fps/50 Hz, 6.0 MHz FM audio) 101 | lines=625 102 | modulation=vsb 103 | sr=15000000 104 | colour=1 105 | audio=1 106 | nicam=1 107 | a2stereo=1 108 | teletext=1 109 | wss=1 110 | vits=1 111 | acp=1 112 | scrambling=1 113 | uhf=uhf_eu 114 | vhf=vhf_ie 115 | vhf2=vhf_za 116 | 117 | [g] 118 | alt=b 119 | name=PAL-B/G (625 lines, 25 fps/50 Hz, 5.5 MHz FM audio) 120 | lines=625 121 | modulation=vsb 122 | sr=15000000 123 | colour=1 124 | audio=1 125 | nicam=1 126 | a2stereo=1 127 | teletext=1 128 | wss=1 129 | vits=1 130 | acp=1 131 | scrambling=1 132 | uhf=uhf_eu 133 | uhf2=uhf_au 134 | uhf3=uhf_nz 135 | vhf=vhf_eu 136 | vhf2=vhf_au 137 | vhf3=vhf_nz 138 | 139 | [pal-d] 140 | alt=pal-k 141 | name=PAL-D/K (625 lines, 25 fps/50 Hz, 6.5 MHz FM audio) 142 | lines=625 143 | modulation=vsb 144 | sr=15000000 145 | colour=1 146 | audio=1 147 | nicam=1 148 | a2stereo=1 149 | teletext=1 150 | wss=1 151 | vits=1 152 | acp=1 153 | scrambling=1 154 | uhf=uhf_cn 155 | vhf=vhf_cn 156 | ; Compatible for OIRT countries that have moved from SECAM-D/K to PAL-D/K 157 | uhf2=uhf_eu 158 | vhf2=vhf_ru 159 | 160 | [pal-fm] 161 | name=PAL-FM (625 lines, 25 fps/50 Hz, 7.02/7.20 MHz FM stereo audio) 162 | lines=625 163 | modulation=fm 164 | sr=20250000 165 | colour=1 166 | audio=1 167 | teletext=1 168 | wss=1 169 | vits=1 170 | acp=1 171 | scrambling=1 172 | sat=astra 173 | 174 | [pal-m] 175 | name=PAL-M (525 lines, 30 fps/60 Hz, 4.5 MHz FM audio) 176 | lines=525 177 | modulation=vsb 178 | sr=13500000 179 | colour=1 180 | audio=1 181 | uhf=uhf_ntsc 182 | vhf=vhf_ntsc 183 | 184 | [pal-n] 185 | name=PAL-N (625 lines, 25 fps/50 Hz, 4.5 MHz FM audio) 186 | lines=625 187 | modulation=vsb 188 | sr=13500000 189 | colour=1 190 | audio=1 191 | teletext=1 192 | wss=1 193 | vits=1 194 | acp=1 195 | scrambling=1 196 | uhf=uhf_ntsc 197 | vhf=vhf_ntsc 198 | 199 | [pal60-i] 200 | ; Non-standard PAL/525 with System I audio spacing 201 | name=PAL colour, 525 lines, 30 fps/60 Hz, 6.0 MHz FM audio 202 | lines=525 203 | modulation=vsb 204 | sr=13500000 205 | colour=1 206 | audio=1 207 | uhf=uhf_eu 208 | vhf=vhf_ie 209 | 210 | [pal] 211 | name=Baseband PAL (625 lines, 25 fps/50 Hz) 212 | lines=625 213 | modulation=baseband 214 | sr=13500000 215 | colour=1 216 | teletext=1 217 | wss=1 218 | vits=1 219 | acp=1 220 | scrambling=1 221 | 222 | [525pal] 223 | name=Baseband PAL (525 lines, 30 fps/60 Hz) 224 | lines=525 225 | modulation=baseband 226 | sr=13500000 227 | colour=1 228 | 229 | [pal60] 230 | ; 525-line baseband PAL with standard 4.433618 MHz colour subcarrier 231 | name=Baseband PAL (525 lines, 30 fps/60 Hz, standard colour subcarrier) 232 | lines=525 233 | modulation=baseband 234 | sr=13500000 235 | colour=1 236 | 237 | ; NTSC and related American modes 238 | [m] 239 | name=NTSC-M (525 lines, 29.97 fps/59.94 Hz, 4.5 MHz FM audio) 240 | lines=525 241 | modulation=vsb 242 | sr=13500000 243 | colour=1 244 | audio=1 245 | a2stereo=1 ; Korean variant 246 | uhf=uhf_ntsc 247 | vhf=vhf_ntsc 248 | uhf2=uhf_jp 249 | vhf2=vhf_jp 250 | 251 | [ntsc-fm] 252 | name=NTSC-FM (525 lines, 29.97 fps/59.94 Hz, 6.5 MHz FM audio) 253 | lines=525 254 | modulation=fm 255 | sr=18000000 256 | colour=1 257 | audio=1 258 | 259 | [ntsc-bs] 260 | name=NTSC-FM BS (525 lines, 29.97 fps/59.94 Hz, BS digital audio) 261 | lines=525 262 | modulation=fm 263 | sr=18000000 264 | colour=1 265 | audio=1 266 | 267 | [ntsc-i] 268 | ; Non-standard NTSC/525 with System I audio spacing 269 | name=NTSC 525 lines, 29.97 fps/59.94 Hz, 6.0 MHz FM audio 270 | lines=525 271 | modulation=vsb 272 | sr=13500000 273 | colour=1 274 | audio=1 275 | uhf=uhf_eu 276 | vhf=vhf_ie 277 | vhf2=vhf_za 278 | 279 | [ntsc-a] 280 | ; NTSC-A based on the 1954 Marconi proposal 281 | ; Was not used except for early tests 282 | name=NTSC-A (405 lines, 25 fps/50 Hz, -3.5 MHz AM audio) 283 | lines=405 284 | modulation=vsb 285 | sr=8100000 286 | colour=1 287 | audio=1 288 | vhf=vhf_gb 289 | 290 | [apollo-fsc-fm] 291 | name=Apollo Field Sequential Color (525 lines, 29.97 fps/59.94 Hz) 292 | lines=525 293 | modulation=fm 294 | sr=18000000 295 | colour=1 296 | audio=1 297 | 298 | [m-cbs405] 299 | name=CBS Field Sequential Color (405 lines, 72 fps/144 Hz) 300 | lines=405 301 | modulation=vsb 302 | sr=18954000 303 | colour=1 304 | audio=1 305 | uhf=uhf_ntsc 306 | vhf=vhf_ntsc 307 | 308 | [ntsc] 309 | name=Baseband NTSC (525 lines, 29.97 fps/59.94 Hz) 310 | lines=525 311 | modulation=baseband 312 | sr=13500000 313 | colour=1 314 | 315 | [ntsc-405] 316 | ; Baseband version of NTSC-A 317 | name=Baseband NTSC (405 lines, 25 fps/50 Hz) 318 | lines=405 319 | modulation=baseband 320 | sr=8100000 321 | colour=1 322 | 323 | [cbs405] 324 | name=Baseband CBS FSC (405 lines, 72 fps/144 Hz) 325 | lines=405 326 | modulation=baseband 327 | sr=18954000 328 | colour=1 329 | 330 | ; SECAM 331 | [l] 332 | name=SECAM-L (625 lines, 25 fps, 6.5 MHz AM audio) 333 | lines=625 334 | modulation=vsb 335 | sr=15000000 336 | colour=1 337 | audio=1 338 | nicam=1 339 | a2stereo=0 340 | teletext=1 341 | wss=1 342 | vits=1 343 | acp=1 344 | scrambling=1 345 | uhf=uhf_eu 346 | vhf=vhf_fr_l 347 | 348 | [d] 349 | alt=k 350 | name=SECAM-D/K (625 lines, 25 fps, 6.5 MHz FM audio) 351 | lines=625 352 | modulation=vsb 353 | sr=15000000 354 | colour=1 355 | audio=1 356 | nicam=0 357 | a2stereo=1 358 | teletext=1 359 | wss=1 360 | vits=1 361 | acp=1 362 | scrambling=1 363 | uhf=uhf_eu 364 | vhf=vhf_ru 365 | 366 | [secam-b] 367 | alt=secam-g 368 | name=SECAM-B/G (625 lines, 25 fps, 5.5 MHz FM audio) 369 | lines=625 370 | modulation=vsb 371 | sr=15000000 372 | colour=1 373 | audio=1 374 | nicam=1 375 | a2stereo=0 376 | teletext=1 377 | wss=1 378 | vits=1 379 | acp=1 380 | scrambling=1 381 | uhf=uhf_eu 382 | vhf=vhf_eu 383 | 384 | [secam-fm] 385 | name=SECAM-FM (625 lines, 25 fps, 7.02/7.20 MHz FM stereo audio) 386 | lines=625 387 | modulation=fm 388 | sr=20250000 389 | colour=1 390 | audio=1 391 | teletext=1 392 | wss=1 393 | vits=1 394 | acp=1 395 | scrambling=1 396 | 397 | [secam-i] 398 | name=SECAM-I (625 lines, 25 fps, 6.0 MHz FM audio) 399 | lines=625 400 | modulation=vsb 401 | sr=15000000 402 | colour=1 403 | audio=1 404 | nicam=1 405 | a2stereo=0 406 | teletext=1 407 | wss=1 408 | vits=1 409 | acp=1 410 | scrambling=1 411 | uhf=uhf_eu 412 | vhf=vhf_ie 413 | 414 | [secam] 415 | name=Baseband SECAM (625 lines, 25 fps) 416 | lines=625 417 | modulation=baseband 418 | sr=13500000 419 | colour=1 420 | teletext=1 421 | wss=1 422 | vits=1 423 | acp=1 424 | scrambling=1 425 | 426 | ; Other modes (mainly black and white) 427 | [a] 428 | name=CCIR System A (405 lines, 25 fps/50 Hz, -3.5 MHz AM audio) 429 | lines=405 430 | modulation=vsb 431 | sr=8100000 432 | audio=1 433 | vhf=vhf_gb 434 | 435 | [e] 436 | name=CCIR System E (819 lines, 25 fps/50 Hz, 11.15 MHz AM audio) 437 | lines=819 438 | modulation=vsb 439 | sr=20250000 440 | audio=1 441 | vhf=vhf_fr_e 442 | 443 | [240-am] 444 | name=Baird mechanical (240 lines, 25 fps) 445 | lines=240 446 | modulation=vsb 447 | sr=1992000 448 | 449 | [30-am] 450 | name=Baird mechanical (30 lines, 12.5 fps) 451 | lines=30 452 | modulation=vsb 453 | sr=100500 454 | 455 | [apollo-fm] 456 | name=Apollo (320 lines, 10 fps, FM) 457 | lines=320 458 | modulation=fm 459 | sr=2048000 460 | 461 | [nbtv-am] 462 | name=NBTV Club standard (32 lines, 12.5 fps, no audio) 463 | lines=32 464 | modulation=vsb 465 | sr=100000 466 | 467 | [405-i] 468 | ; Non-standard 405 line variant, negatively modulated with System I-style FM audio carrier 469 | ; This was never used but works on Sony TV9-90UB sets with both VHF and UHF buttons pressed in 470 | ; Since this mode switches the tuner to UHF, we'll only enable the European UHF bandplan 471 | name=405 lines, 25 fps/50 Hz, +6.0 MHz FM audio 472 | lines=405 473 | modulation=vsb 474 | sr=8100000 475 | audio=1 476 | uhf=uhf_eu 477 | 478 | [405] 479 | name=Baseband 405 lines, 25 fps/50 Hz 480 | lines=405 481 | modulation=baseband 482 | sr=8100000 483 | 484 | [819] 485 | name=Baseband 819 lines, 25 fps/50 Hz 486 | lines=819 487 | modulation=baseband 488 | sr=20250000 489 | 490 | [240] 491 | name=Baseband Baird 240 lines, 25 fps 492 | lines=240 493 | modulation=baseband 494 | sr=1992000 495 | 496 | [30] 497 | name=Baseband Baird 30 lines, 12.5 fps 498 | lines=405 499 | modulation=baseband 500 | sr=100500 501 | 502 | [apollo] 503 | name=Baseband Apollo (320 lines, 10 fps) 504 | lines=405 505 | modulation=baseband 506 | sr=2048000 507 | 508 | [nbtv] 509 | name=Baseband NBTV Club standard (32 lines, 12.5 fps) 510 | lines=405 511 | modulation=baseband 512 | sr=100000 513 | 514 | ; MAC 515 | [d2mac-am] 516 | name=D2-MAC (625 lines, 25 fps/50 Hz, AM, digital audio) 517 | lines=625 518 | modulation=vsb 519 | sr=20250000 520 | audio=1 521 | teletext=1 522 | scrambling=1 523 | 524 | [d2mac-fm] 525 | name=D2-MAC (625 lines, 25 fps/50 Hz, FM, digital audio) 526 | lines=625 527 | modulation=fm 528 | sr=20250000 529 | audio=1 530 | teletext=1 531 | scrambling=1 532 | 533 | [dmac-am] 534 | name=D-MAC (625 lines, 25 fps/50 Hz, AM, digital audio) 535 | lines=625 536 | modulation=vsb 537 | sr=20250000 538 | audio=1 539 | teletext=1 540 | scrambling=1 541 | 542 | [dmac-fm] 543 | name=D-MAC (625 lines, 25 fps/50 Hz, FM, digital audio) 544 | lines=625 545 | modulation=fm 546 | sr=20250000 547 | audio=1 548 | teletext=1 549 | scrambling=1 550 | sat=bsb 551 | 552 | [d2mac] 553 | name=Baseband D2-MAC (625 lines, 25 fps/50 Hz) 554 | lines=625 555 | modulation=baseband 556 | sr=20250000 557 | teletext=1 558 | scrambling=1 559 | 560 | [dmac] 561 | name=Baseband D-MAC (625 lines, 25 fps/50 Hz) 562 | lines=625 563 | modulation=baseband 564 | sr=20250000 565 | teletext=1 566 | scrambling=1 567 | -------------------------------------------------------------------------------- /src/com/steeviebops/resources/flatlaf.ini: -------------------------------------------------------------------------------- 1 | [flatlaf] 2 | Version=3.5.4 3 | UseWindowDecorations=1 4 | MenuBarEmbedded=0 5 | UseRoundedPopupBorder=0 6 | 7 | [core-themes] 8 | class=com.formdev.flatlaf 9 | FlatLightLaf=Light 10 | FlatDarkLaf=Dark 11 | FlatIntelliJLaf=IntelliJ-style 12 | FlatDarculaLaf=Darcula-style 13 | 14 | [core-themes-v3] 15 | class=com.formdev.flatlaf.themes 16 | FlatMacLightLaf=macOS Light 17 | FlatMacDarkLaf=macOS Dark 18 | 19 | [intellij-themes] 20 | class=com.formdev.flatlaf.intellijthemes 21 | FlatArcIJTheme=Arc 22 | FlatArcOrangeIJTheme=Arc - Orange 23 | FlatArcDarkIJTheme=Arc Dark 24 | FlatArcDarkOrangeIJTheme=Arc Dark - Orange 25 | FlatCarbonIJTheme=Carbon 26 | FlatCobalt2IJTheme=Cobalt 2 27 | FlatCyanLightIJTheme=Cyan light 28 | FlatDarkFlatIJTheme=Dark Flat 29 | FlatDarkPurpleIJTheme=Dark purple 30 | FlatDraculaIJTheme=Dracula 31 | FlatGradiantoDarkFuchsiaIJTheme=Gradianto Dark Fuchsia 32 | FlatGradiantoDeepOceanIJTheme=Gradianto Deep Ocean 33 | FlatGradiantoMidnightBlueIJTheme=Gradianto Midnight Blue 34 | FlatGradiantoNatureGreenIJTheme=Gradianto Nature Green 35 | FlatGrayIJTheme=Gray 36 | FlatGruvboxDarkHardIJTheme=Gruvbox Dark Hard 37 | FlatHiberbeeDarkIJTheme=Hiberbee Dark 38 | FlatHighContrastIJTheme=High Contrast 39 | FlatLightFlatIJTheme=Light Flat 40 | FlatMaterialDesignDarkIJTheme=Material Design Dark 41 | FlatMonocaiIJTheme=Monocai 42 | FlatMonokaiProIJTheme=Monokai Pro 43 | FlatNordIJTheme=Nord 44 | FlatOneDarkIJTheme=One Dark 45 | FlatSolarizedDarkIJTheme=Solarized Dark 46 | FlatSolarizedLightIJTheme=Solarized Light 47 | FlatSpacegrayIJTheme=Spacegray 48 | FlatVuesionIJTheme=Vuesion 49 | FlatXcodeDarkIJTheme=Xcode-Dark 50 | 51 | [materialthemeuilite] 52 | class=com.formdev.flatlaf.intellijthemes.materialthemeuilite 53 | FlatArcDarkIJTheme=Arc Dark (Material) 54 | FlatAtomOneDarkIJTheme=Atom One Dark - Material 55 | FlatAtomOneLightIJTheme=Atom One Light - Material 56 | FlatDraculaIJTheme=Dracula - Material 57 | FlatGitHubIJTheme=GitHub - Material 58 | FlatGitHubDarkIJTheme=GitHub Dark - Material 59 | FlatLightOwlIJTheme=Light Owl - Material 60 | FlatMaterialDarkerIJTheme=Material Darker - Material 61 | FlatMaterialDeepOceanIJTheme=Material Deep Ocean - Material 62 | FlatMaterialLighterIJTheme=Material Lighter - Material 63 | FlatMaterialOceanicIJTheme=Material Oceanic - Material 64 | FlatMaterialPalenightIJTheme=Material Palenight - Material 65 | FlatMonokaiProIJTheme=Monokai Pro - Material 66 | FlatMoonlightIJTheme=Moonlight - Material 67 | FlatNightOwlIJTheme=Night Owl - Material 68 | FlatSolarizedDarkIJTheme=Solarized Dark - Material 69 | FlatSolarizedLightIJTheme=Solarized Light - Material 70 | -------------------------------------------------------------------------------- /src/com/steeviebops/resources/fsphil.ini: -------------------------------------------------------------------------------- 1 | [Modes.ini] 2 | FileVersion=5.5 3 | 4 | [videomodes] 5 | pal=i, g, pal-d, pal-fm, pal-n, pal-m, pal60-i, pal, pal60, 525pal 6 | ntsc=m, ntsc-fm, ntsc-bs, ntsc-i, ntsc-a, apollo-fsc-fm, m-cbs405, ntsc, ntsc-405, cbs405 7 | secam=l, d, secam-b, secam-fm, secam-i, secam 8 | other=a, e, 240-am, 30-am, apollo-fm, nbtv-am, 405-i, 405, 819, 240, 30, apollo, nbtv 9 | mac=d2mac-fm, dmac-fm, d2mac-am, dmac-am, d2mac, dmac 10 | 11 | [videocrypt] 12 | free=Free access/soft scrambled (no card required) 13 | conditional=Conditional access (Sky 11 card) 14 | 15 | [videocrypt2] 16 | free=Free access/soft scrambled (no card required) 17 | 18 | [videocrypts] 19 | free=Free access/soft scrambled (no card required) 20 | conditional=Conditional access (BBC Select card) 21 | 22 | [syster] 23 | ; If no parameter is to be specified on the command line, enter the word "blank" (without the quotation marks) 24 | ; This is treated as a placeholder and will be removed automatically 25 | blank=Free access 26 | 27 | [eurocrypt] 28 | ; If no parameter is to be specified on the command line, enter "blank" instead 29 | ; This is used as a placeholder and will be removed automatically 30 | blank=No conditional access (free) 31 | bbcprime=EuroCrypt M (BBC Prime) 32 | cplusfr=EuroCrypt M (Canal+ France 4:3) 33 | cplusfr169=EuroCrypt M (Canal+ France 16:9) 34 | ctv=EuroCrypt M (CTV) 35 | filmnet=EuroCrypt M (FilmNet) 36 | multivisio=EuroCrypt M (Multivisio) 37 | tv3=EuroCrypt M (TV3) 38 | tv1000=EuroCrypt M (TV 1000) 39 | tvcable=EuroCrypt M (TV Cable) 40 | tvplus=EuroCrypt M (TVPlus) 41 | visiopass=EuroCrypt M (Visiopass) 42 | teletv=EuroCrypt S (Tele-TV) 43 | ctvs=EuroCrypt S2 (CTV) 44 | eros=EuroCrypt S2 (Eros) 45 | nrk=EuroCrypt S2 (NRK) 46 | rdv=EuroCrypt S2 (RDV) 47 | tv2=EuroCrypt S2 (TV2 Norway) 48 | tvs=EuroCrypt S2 (TV-S) 49 | cplus=EuroCrypt 3DES (Canal+ Denmark) 50 | 51 | [testsignals_625_pal] 52 | ; The syntax of this section is as follows: 53 | ; Command line argument=Friendly name, pattern filename, text insertion supported, sample rate 54 | ; Sample rate defaults to 13.5 MHz if not defined 55 | colourbars=hacktv colour bars 56 | cbar=EBU colour bars,ebu_cbar_pal.bin,0 57 | philips4x3=Philips 4:3,philips_4x3_pal.bin,1 58 | philips16x9=Philips 16:9,philips_16x9_pal.bin,1 59 | fubk4x3=FuBK 4:3,fubk_4x3.bin,1 60 | fubk16x9=FuBK 16:9,fubk_16x9_pal.bin,1 61 | philipsih=Indian Head,philips_indian_head.bin,0,20 62 | pulseandbar=Pulse and bar,pulse_bar_pal.bin,0 63 | sinxx=Sinx/x,sin_x_x_pal.bin,0 64 | 65 | [testsignals_525_ntsc] 66 | colourbars=hacktv colour bars 67 | cbar=SMPTE bars,smtpe_cbar_ntsc.bin,1 68 | philips4x3=Philips 4:3,philips_4x3_ntsc.bin,1 69 | philips16x9=Philips 16:9,philips_16x9_ntsc.bin,1 70 | 71 | [testsignals_625_secam] 72 | colourbars=hacktv colour bars 73 | philips4x3=Philips 4:3,philips_4x3_secam.bin,1 74 | 75 | ; PAL 76 | [i] 77 | name=PAL-I (625 lines, 25 fps/50 Hz, 6.0 MHz FM audio) 78 | lines=625 79 | modulation=vsb 80 | sr=15000000 81 | colour=1 82 | audio=1 83 | nicam=1 84 | a2stereo=1 85 | teletext=1 86 | wss=1 87 | vits=1 88 | acp=1 89 | scrambling=1 90 | uhf=uhf_eu 91 | vhf=vhf_ie 92 | vhf2=vhf_za 93 | 94 | [g] 95 | alt=b 96 | name=PAL-B/G (625 lines, 25 fps/50 Hz, 5.5 MHz FM audio) 97 | lines=625 98 | modulation=vsb 99 | sr=15000000 100 | colour=1 101 | audio=1 102 | nicam=1 103 | a2stereo=1 104 | teletext=1 105 | wss=1 106 | vits=1 107 | acp=1 108 | scrambling=1 109 | uhf=uhf_eu 110 | uhf2=uhf_au 111 | uhf3=uhf_nz 112 | vhf=vhf_eu 113 | vhf2=vhf_au 114 | vhf3=vhf_nz 115 | 116 | [pal-d] 117 | alt=pal-k 118 | name=PAL-D/K (625 lines, 25 fps/50 Hz, 6.5 MHz FM audio) 119 | lines=625 120 | modulation=vsb 121 | sr=15000000 122 | colour=1 123 | audio=1 124 | nicam=1 125 | a2stereo=1 126 | teletext=1 127 | wss=1 128 | vits=1 129 | acp=1 130 | scrambling=1 131 | uhf=uhf_cn 132 | vhf=vhf_cn 133 | ; Compatible for OIRT countries that have moved from SECAM-D/K to PAL-D/K 134 | uhf2=uhf_eu 135 | vhf2=vhf_ru 136 | 137 | [pal-fm] 138 | name=PAL-FM (625 lines, 25 fps/50 Hz, 6.5 MHz FM audio) 139 | lines=625 140 | modulation=fm 141 | sr=20250000 142 | colour=1 143 | audio=1 144 | teletext=1 145 | wss=1 146 | vits=1 147 | acp=1 148 | scrambling=1 149 | sat=astra 150 | 151 | [pal-m] 152 | name=PAL-M (525 lines, 30 fps/60 Hz, 4.5 MHz FM audio) 153 | lines=525 154 | modulation=vsb 155 | sr=13500000 156 | colour=1 157 | audio=1 158 | uhf=uhf_ntsc 159 | vhf=vhf_ntsc 160 | 161 | [pal-n] 162 | name=PAL-N (625 lines, 25 fps/50 Hz, 4.5 MHz FM audio) 163 | lines=625 164 | modulation=vsb 165 | sr=13500000 166 | colour=1 167 | audio=1 168 | teletext=1 169 | wss=1 170 | vits=1 171 | acp=1 172 | scrambling=1 173 | uhf=uhf_ntsc 174 | vhf=vhf_ntsc 175 | 176 | [pal60-i] 177 | ; Non-standard PAL/525 with System I audio spacing 178 | name=PAL colour, 525 lines, 30 fps/60 Hz, 6.0 MHz FM audio 179 | lines=525 180 | modulation=vsb 181 | sr=13500000 182 | colour=1 183 | audio=1 184 | uhf=uhf_eu 185 | vhf=vhf_ie 186 | 187 | [pal] 188 | name=Baseband PAL (625 lines, 25 fps/50 Hz) 189 | lines=625 190 | modulation=baseband 191 | sr=13500000 192 | colour=1 193 | teletext=1 194 | wss=1 195 | vits=1 196 | acp=1 197 | scrambling=1 198 | 199 | [525pal] 200 | ; 525-line baseband PAL with 3.57611 MHz colour subcarrier (same as PAL-M) 201 | name=Baseband PAL (525 lines, 30 fps/60 Hz, PAL-M colour subcarrier) 202 | lines=525 203 | modulation=baseband 204 | sr=13500000 205 | colour=1 206 | 207 | [pal60] 208 | ; 525-line baseband PAL with standard 4.433618 MHz colour subcarrier 209 | name=Baseband PAL (525 lines, 30 fps/60 Hz, standard colour subcarrier) 210 | lines=525 211 | modulation=baseband 212 | sr=13500000 213 | colour=1 214 | 215 | ; NTSC and related American modes 216 | [m] 217 | name=NTSC-M (525 lines, 29.97 fps/59.94 Hz, 4.5 MHz FM audio) 218 | lines=525 219 | modulation=vsb 220 | sr=13500000 221 | colour=1 222 | audio=1 223 | a2stereo=1 ; Korean variant 224 | uhf=uhf_ntsc 225 | vhf=vhf_ntsc 226 | uhf2=uhf_jp 227 | vhf2=vhf_jp 228 | 229 | [ntsc-fm] 230 | name=NTSC-FM (525 lines, 29.97 fps/59.94 Hz, 6.5 MHz FM audio) 231 | lines=525 232 | modulation=fm 233 | sr=18000000 234 | colour=1 235 | audio=1 236 | 237 | [ntsc-bs] 238 | name=NTSC-FM BS (525 lines, 29.97 fps/59.94 Hz, BS digital audio) 239 | lines=525 240 | modulation=fm 241 | sr=18000000 242 | colour=1 243 | audio=1 244 | 245 | [ntsc-i] 246 | ; Non-standard NTSC/525 with System I audio spacing 247 | name=NTSC colour, 525 lines, 29.97 fps/59.94 Hz, 6.0 MHz FM audio 248 | lines=525 249 | modulation=vsb 250 | sr=13500000 251 | colour=1 252 | audio=1 253 | uhf=uhf_eu 254 | vhf=vhf_ie 255 | vhf2=vhf_za 256 | 257 | [ntsc-a] 258 | ; NTSC-A based on the 1954 Marconi proposal 259 | ; Was not used except for early tests 260 | name=NTSC-A (405 lines, 25 fps/50 Hz, -3.5 MHz AM audio) 261 | lines=405 262 | modulation=vsb 263 | sr=8100000 264 | colour=1 265 | audio=1 266 | vhf=vhf_gb 267 | 268 | [apollo-fsc-fm] 269 | name=Apollo Field Sequential Color (525 lines, 29.97 fps/59.94 Hz) 270 | lines=525 271 | modulation=fm 272 | sr=18000000 273 | colour=1 274 | audio=1 275 | 276 | [m-cbs405] 277 | name=CBS Field Sequential Color (405 lines, 72 fps/144 Hz) 278 | lines=405 279 | modulation=vsb 280 | sr=18954000 281 | colour=1 282 | audio=1 283 | uhf=uhf_ntsc 284 | vhf=vhf_ntsc 285 | 286 | [ntsc] 287 | name=Baseband NTSC (525 lines, 29.97 fps/59.94 Hz) 288 | lines=525 289 | modulation=baseband 290 | sr=13500000 291 | colour=1 292 | 293 | [ntsc-405] 294 | ; Baseband version of NTSC-A 295 | name=Baseband NTSC (405 lines, 25 fps/50 Hz) 296 | lines=405 297 | modulation=baseband 298 | sr=8100000 299 | colour=1 300 | 301 | [cbs405] 302 | name=Baseband CBS FSC (405 lines, 72 fps/144 Hz) 303 | lines=405 304 | modulation=baseband 305 | sr=18954000 306 | colour=1 307 | 308 | ; SECAM 309 | [l] 310 | name=SECAM-L (625 lines, 25 fps, 6.5 MHz AM audio) 311 | lines=625 312 | modulation=vsb 313 | sr=15000000 314 | colour=1 315 | audio=1 316 | nicam=1 317 | a2stereo=0 318 | teletext=1 319 | wss=1 320 | vits=1 321 | acp=1 322 | scrambling=1 323 | uhf=uhf_eu 324 | vhf=vhf_fr_l 325 | 326 | [d] 327 | alt=k 328 | name=SECAM-D/K (625 lines, 25 fps, 6.5 MHz FM audio) 329 | lines=625 330 | modulation=vsb 331 | sr=15000000 332 | colour=1 333 | audio=1 334 | nicam=0 335 | a2stereo=1 336 | teletext=1 337 | wss=1 338 | vits=1 339 | acp=1 340 | scrambling=1 341 | uhf=uhf_eu 342 | vhf=vhf_ru 343 | 344 | [secam-b] 345 | alt=secam-g 346 | name=SECAM-B/G (625 lines, 25 fps, 5.5 MHz FM audio) 347 | lines=625 348 | modulation=vsb 349 | sr=15000000 350 | colour=1 351 | audio=1 352 | nicam=1 353 | a2stereo=0 354 | teletext=1 355 | wss=1 356 | vits=1 357 | acp=1 358 | scrambling=1 359 | uhf=uhf_eu 360 | vhf=vhf_eu 361 | 362 | [secam-fm] 363 | name=SECAM-FM (625 lines, 25 fps, 6.5 MHz FM audio) 364 | lines=625 365 | modulation=fm 366 | sr=20250000 367 | colour=1 368 | audio=1 369 | teletext=1 370 | wss=1 371 | vits=1 372 | acp=1 373 | scrambling=1 374 | 375 | [secam-i] 376 | name=SECAM-I (625 lines, 25 fps, 6.0 MHz FM audio) 377 | lines=625 378 | modulation=vsb 379 | sr=15000000 380 | colour=1 381 | audio=1 382 | nicam=1 383 | a2stereo=0 384 | teletext=1 385 | wss=1 386 | vits=1 387 | acp=1 388 | scrambling=1 389 | uhf=uhf_eu 390 | vhf=vhf_ie 391 | 392 | [secam] 393 | name=Baseband SECAM (625 lines, 25 fps) 394 | lines=625 395 | modulation=baseband 396 | sr=13500000 397 | colour=1 398 | teletext=1 399 | wss=1 400 | vits=1 401 | acp=1 402 | scrambling=1 403 | 404 | ; Other modes (mainly black and white) 405 | [a] 406 | name=CCIR System A (405 lines, 25 fps/50 Hz, -3.5 MHz AM audio) 407 | lines=405 408 | modulation=vsb 409 | sr=8100000 410 | audio=1 411 | vhf=vhf_gb 412 | 413 | [e] 414 | name=CCIR System E (819 lines, 25 fps/50 Hz, 11.15 MHz AM audio) 415 | lines=819 416 | modulation=vsb 417 | sr=20250000 418 | audio=1 419 | vhf=vhf_fr_e 420 | 421 | [240-am] 422 | name=Baird mechanical (240 lines, 25 fps) 423 | lines=240 424 | modulation=vsb 425 | sr=1992000 426 | 427 | [30-am] 428 | name=Baird mechanical (30 lines, 12.5 fps) 429 | lines=30 430 | modulation=vsb 431 | sr=100500 432 | 433 | [apollo-fm] 434 | name=Apollo (320 lines, 10 fps, FM) 435 | lines=320 436 | modulation=fm 437 | sr=2048000 438 | 439 | [nbtv-am] 440 | name=NBTV Club standard (32 lines, 12.5 fps, no audio) 441 | lines=32 442 | modulation=vsb 443 | sr=100000 444 | 445 | [405-i] 446 | ; Non-standard 405 line variant, negatively modulated with System I-style FM audio carrier 447 | ; This was never used but works on Sony TV9-90UB sets with both VHF and UHF buttons pressed in 448 | ; Since this mode switches the tuner to UHF, we'll only enable the European UHF bandplan 449 | name=405 lines, 25 fps/50 Hz, +6.0 MHz FM audio 450 | lines=405 451 | modulation=vsb 452 | sr=8100000 453 | audio=1 454 | uhf=uhf_eu 455 | 456 | [405] 457 | name=Baseband 405 lines, 25 fps/50 Hz 458 | lines=405 459 | modulation=baseband 460 | sr=8100000 461 | 462 | [819] 463 | name=Baseband 819 lines, 25 fps/50 Hz 464 | lines=819 465 | modulation=baseband 466 | sr=20250000 467 | 468 | [240] 469 | name=Baseband Baird 240 lines, 25 fps 470 | lines=240 471 | modulation=baseband 472 | sr=1992000 473 | 474 | [30] 475 | name=Baseband Baird 30 lines, 12.5 fps 476 | lines=405 477 | modulation=baseband 478 | sr=100500 479 | 480 | [apollo] 481 | name=Baseband Apollo (320 lines, 10 fps) 482 | lines=405 483 | modulation=baseband 484 | sr=2048000 485 | 486 | [nbtv] 487 | name=Baseband NBTV Club standard (32 lines, 12.5 fps) 488 | lines=405 489 | modulation=baseband 490 | sr=100000 491 | 492 | ; MAC 493 | [d2mac-am] 494 | name=D2-MAC (625 lines, 25 fps/50 Hz, AM, digital audio) 495 | lines=625 496 | modulation=vsb 497 | sr=20250000 498 | audio=1 499 | teletext=1 500 | scrambling=1 501 | 502 | [d2mac-fm] 503 | name=D2-MAC (625 lines, 25 fps/50 Hz, FM, digital audio) 504 | lines=625 505 | modulation=fm 506 | sr=20250000 507 | audio=1 508 | teletext=1 509 | scrambling=1 510 | 511 | [dmac-am] 512 | name=D-MAC (625 lines, 25 fps/50 Hz, AM, digital audio) 513 | lines=625 514 | modulation=vsb 515 | sr=20250000 516 | audio=1 517 | teletext=1 518 | scrambling=1 519 | 520 | [dmac-fm] 521 | name=D-MAC (625 lines, 25 fps/50 Hz, FM, digital audio) 522 | lines=625 523 | modulation=fm 524 | sr=20250000 525 | audio=1 526 | teletext=1 527 | scrambling=1 528 | sat=bsb 529 | 530 | [d2mac] 531 | name=Baseband D2-MAC (625 lines, 25 fps/50 Hz) 532 | lines=625 533 | modulation=baseband 534 | sr=20250000 535 | teletext=1 536 | scrambling=1 537 | 538 | [dmac] 539 | name=Baseband D-MAC (625 lines, 25 fps/50 Hz) 540 | lines=625 541 | modulation=baseband 542 | sr=20250000 543 | teletext=1 544 | scrambling=1 545 | -------------------------------------------------------------------------------- /src/com/steeviebops/resources/test.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/steeviebops/hacktv-gui/0924bd1c6201ceb0b961503af572f84755771312/src/com/steeviebops/resources/test.gif --------------------------------------------------------------------------------