aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-02-13 13:25:39 +0100
committerJohn Ankarström <john@ankarstrom.se>2022-02-13 13:25:39 +0100
commit92f7852b6979239214848b7ebb4046d52e4aba06 (patch)
treedf49eb01bc34c2acebdc3ad42f505cdb64afdae9
downloadEpisodeBrowser-92f7852b6979239214848b7ebb4046d52e4aba06.tar.gz
Begin Win32 re-implementation.
-rw-r--r--.cproject19
-rw-r--r--.gitignore1
-rw-r--r--.project20
-rw-r--r--.settings/org.eclipse.cdt.codan.core.prefs109
-rw-r--r--.settings/org.eclipse.cdt.core.prefs6
-rw-r--r--Makefile31
-rw-r--r--atom_dcg.pl11
-rw-r--r--episode_data.db189
-rw-r--r--episode_data.pl68
-rw-r--r--local_episodes.pl31
-rw-r--r--mk.bat4
-rw-r--r--track_episodes.db212
-rw-r--r--track_episodes.pl96
-rw-r--r--win.c122
14 files changed, 919 insertions, 0 deletions
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..10421ac
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?><cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+ <storageModule moduleId="org.eclipse.cdt.core.settings">
+ <cconfiguration id="org.eclipse.cdt.core.default.config.2131462018">
+ <storageModule buildSystemId="org.eclipse.cdt.core.defaultConfigDataProvider" id="org.eclipse.cdt.core.default.config.2131462018" moduleId="org.eclipse.cdt.core.settings" name="Configuration">
+ <externalSettings/>
+ <extensions/>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+ </cconfiguration>
+ </storageModule>
+ <storageModule moduleId="org.eclipse.cdt.core.LanguageSettingsProviders"/>
+ <storageModule moduleId="org.eclipse.cdt.internal.ui.text.commentOwnerProjectMappings"/>
+ <storageModule moduleId="org.eclipse.cdt.core.pathentry">
+ <pathentry include="C:/Program Files/swipl/include" kind="inc" path="" system="true"/>
+ <pathentry kind="src" path=""/>
+ <pathentry kind="out" path="build"/>
+ </storageModule>
+</cproject> \ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..84c048a
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+/build/
diff --git a/.project b/.project
new file mode 100644
index 0000000..a58c79d
--- /dev/null
+++ b/.project
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>episode_browser</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.cdt.core.cBuilder</name>
+ <triggers>clean,full,incremental,</triggers>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.cdt.core.cnature</nature>
+ <nature>org.eclipse.cdt.core.ccnature</nature>
+ <nature>org.eclipse.cdt.make.core.makeNature</nature>
+ </natures>
+</projectDescription>
diff --git a/.settings/org.eclipse.cdt.codan.core.prefs b/.settings/org.eclipse.cdt.codan.core.prefs
new file mode 100644
index 0000000..da30759
--- /dev/null
+++ b/.settings/org.eclipse.cdt.codan.core.prefs
@@ -0,0 +1,109 @@
+eclipse.preferences.version=1
+org.eclipse.cdt.codan.checkers.errnoreturn=Warning
+org.eclipse.cdt.codan.checkers.errnoreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return\\")",implicit\=>false}
+org.eclipse.cdt.codan.checkers.errreturnvalue=Error
+org.eclipse.cdt.codan.checkers.errreturnvalue.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused return value\\")"}
+org.eclipse.cdt.codan.checkers.localvarreturn=-Warning
+org.eclipse.cdt.codan.checkers.localvarreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Returning the address of a local variable\\")"}
+org.eclipse.cdt.codan.checkers.nocommentinside=-Error
+org.eclipse.cdt.codan.checkers.nocommentinside.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Nesting comments\\")"}
+org.eclipse.cdt.codan.checkers.nolinecomment=-Error
+org.eclipse.cdt.codan.checkers.nolinecomment.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Line comments\\")"}
+org.eclipse.cdt.codan.checkers.noreturn=Error
+org.eclipse.cdt.codan.checkers.noreturn.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No return value\\")",implicit\=>false}
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation=Error
+org.eclipse.cdt.codan.internal.checkers.AbstractClassCreation.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Abstract class cannot be instantiated\\")"}
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AmbiguousProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Ambiguous problem\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.AssignmentInConditionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment in condition\\")"}
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem=Error
+org.eclipse.cdt.codan.internal.checkers.AssignmentToItselfProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Assignment to itself\\")"}
+org.eclipse.cdt.codan.internal.checkers.BlacklistProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.BlacklistProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function or method is blacklisted\\")",blacklist\=>()}
+org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.CStyleCastProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"C-Style cast instead of C++ cast\\")",checkMacro\=>true}
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.CaseBreakProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"No break at end of case\\")",no_break_comment\=>"no break",last_case_param\=>false,empty_case_param\=>false,enable_fallthrough_quickfix_param\=>false}
+org.eclipse.cdt.codan.internal.checkers.CatchByReference=Warning
+org.eclipse.cdt.codan.internal.checkers.CatchByReference.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Catching by reference is recommended\\")",unknown\=>false,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem=Error
+org.eclipse.cdt.codan.internal.checkers.CircularReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Circular inheritance\\")"}
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization=Warning
+org.eclipse.cdt.codan.internal.checkers.ClassMembersInitialization.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class members should be properly initialized\\")",skip\=>true}
+org.eclipse.cdt.codan.internal.checkers.CopyrightProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.CopyrightProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Lack of copyright information\\")",regex\=>".*Copyright.*"}
+org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem=Error
+org.eclipse.cdt.codan.internal.checkers.DecltypeAutoProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid 'decltype(auto)' specifier\\")"}
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FieldResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Field cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.FloatCompareProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.FloatCompareProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Direct float comparison\\")"}
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.FunctionResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Function cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.GotoStatementProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Goto statement used\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidArguments.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid arguments\\")"}
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem=Error
+org.eclipse.cdt.codan.internal.checkers.InvalidTemplateArgumentsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid template argument\\")"}
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.LabelStatementNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Label statement not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MagicNumberProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MagicNumberProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Avoid magic numbers\\")",checkArray\=>true,checkOperatorParen\=>true,exceptions\=>(1,0,-1,2,1.0,0.0,-1.0)}
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MemberDeclarationNotFoundProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Member declaration not found\\")"}
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.MethodResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Method cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissCaseProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissCaseProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing cases in switch\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissDefaultProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing default in switch\\")",defaultWithAllEnums\=>false}
+org.eclipse.cdt.codan.internal.checkers.MissReferenceProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissReferenceProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing reference return value in assignment operator\\")"}
+org.eclipse.cdt.codan.internal.checkers.MissSelfCheckProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MissSelfCheckProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Missing self check in assignment operator\\")"}
+org.eclipse.cdt.codan.internal.checkers.MultipleDeclarationsProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.MultipleDeclarationsProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Multiple variable declaration\\")"}
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker=-Info
+org.eclipse.cdt.codan.internal.checkers.NamingConventionFunctionChecker.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Name convention for function\\")",pattern\=>"^[a-z]",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.NoDiscardProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NoDiscardProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return value not evaluated\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.NonVirtualDestructorProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Class has a virtual method and non-virtual destructor\\")"}
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem=Error
+org.eclipse.cdt.codan.internal.checkers.OverloadProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid overload\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redeclaration\\")"}
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.RedefinitionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Invalid redefinition\\")"}
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ReturnStyleProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Return with parenthesis\\")"}
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ScanfFormatStringSecurityProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Format String Vulnerability\\")"}
+org.eclipse.cdt.codan.internal.checkers.ShallowCopyProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.ShallowCopyProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Miss copy constructor or assignment operator\\")",onlynew\=>false}
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.StatementHasNoEffectProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Statement has no effect\\")",macro\=>true,exceptions\=>()}
+org.eclipse.cdt.codan.internal.checkers.StaticVariableInHeaderProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.StaticVariableInHeaderProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Static variable in header file\\")"}
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuggestedParenthesisProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suggested parenthesis around expression\\")",paramNot\=>false}
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.SuspiciousSemicolonProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Suspicious semicolon\\")",else\=>false,afterelse\=>false}
+org.eclipse.cdt.codan.internal.checkers.SymbolShadowingProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.SymbolShadowingProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol shadowing\\")",paramFuncParameters\=>true}
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.TypeResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Type cannot be resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedFunctionDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused function declaration\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedStaticFunctionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused static function\\")",macro\=>true}
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem=Warning
+org.eclipse.cdt.codan.internal.checkers.UnusedVariableDeclarationProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Unused variable declaration in file scope\\")",macro\=>true,exceptions\=>("@(\#)","$Id")}
+org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem=-Warning
+org.eclipse.cdt.codan.internal.checkers.UsingInHeaderProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Using directive in header\\")"}
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem=Error
+org.eclipse.cdt.codan.internal.checkers.VariableResolutionProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Symbol is not resolved\\")"}
+org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem=-Error
+org.eclipse.cdt.codan.internal.checkers.VirtualMethodCallProblem.params={launchModes\=>{RUN_ON_FULL_BUILD\=>true,RUN_ON_INC_BUILD\=>true,RUN_ON_FILE_OPEN\=>false,RUN_ON_FILE_SAVE\=>false,RUN_AS_YOU_TYPE\=>true,RUN_ON_DEMAND\=>true},suppression_comment\=>"@suppress(\\"Virtual method call in constructor/destructor\\")"}
diff --git a/.settings/org.eclipse.cdt.core.prefs b/.settings/org.eclipse.cdt.core.prefs
new file mode 100644
index 0000000..c8ec5df
--- /dev/null
+++ b/.settings/org.eclipse.cdt.core.prefs
@@ -0,0 +1,6 @@
+doxygen/doxygen_new_line_after_brief=true
+doxygen/doxygen_use_brief_tag=false
+doxygen/doxygen_use_javadoc_tags=true
+doxygen/doxygen_use_pre_tag=false
+doxygen/doxygen_use_structural_commands=false
+eclipse.preferences.version=1
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..bf04586
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,31 @@
+PROJECT_ROOT = $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
+
+INPUTS = $(PROJECT_ROOT)win.c
+INPUTS += $(PROJECT_ROOT)track_episodes.pl
+INPUTS += $(PROJECT_ROOT)local_episodes.pl
+INPUTS += $(PROJECT_ROOT)episode_data.pl
+
+ifeq ($(BUILD_MODE),debug)
+ CFLAGS += -g
+else ifeq ($(BUILD_MODE),run)
+ CFLAGS += -O2
+else ifeq ($(BUILD_MODE),linuxtools)
+ CFLAGS += -g -pg -fprofile-arcs -ftest-coverage
+ LDFLAGS += -pg -fprofile-arcs -ftest-coverage
+ EXTRA_CLEAN += episode_browser.gcda episode_browser.gcno $(PROJECT_ROOT)gmon.out
+ EXTRA_CMDS = rm -rf episode_browser.gcda
+else
+ $(error Build mode $(BUILD_MODE) not supported by this Makefile)
+endif
+
+all: episode_browser.exe
+
+episode_browser.exe: $(INPUTS)
+ swipl-ld -v $(CFLAGS) $(LDFLAGS) -goal true -o $@ $(INPUTS)
+ $(EXTRA_CMDS)
+
+#%.o: $(PROJECT_ROOT)%.c
+# $(CC) -c $(CFLAGS) -o $@ $<
+
+clean:
+ rm -fr episode_browser.exe $(EXTRA_CLEAN)
diff --git a/atom_dcg.pl b/atom_dcg.pl
new file mode 100644
index 0000000..74689d5
--- /dev/null
+++ b/atom_dcg.pl
@@ -0,0 +1,11 @@
+:- module(atom_dcg, [atom_phrase/2]).
+
+:- meta_predicate atom_phrase(2, ?).
+
+atom_phrase(G, A) :-
+ ( var(A)
+ -> phrase(G, C),
+ atom_codes(A, C)
+ ; atom_codes(A, C),
+ phrase(G, C)
+ ).
diff --git a/episode_data.db b/episode_data.db
new file mode 100644
index 0000000..0e02a6d
--- /dev/null
+++ b/episode_data.db
@@ -0,0 +1,189 @@
+created(1643728218.156183).
+assert(episode_name_data(200,'Kogoro Mouri, Suspect (Part 2)',[hint('Hayashi rice / Room service')])).
+assert(episode_name_data(201,'The Tenth Passenger (Part 1)',[hint('Plastic bottle')])).
+assert(episode_name_data(203,'The Black Wings of Icarus (Part 1)',[hint('Bento (Lunch box)')])).
+assert(episode_name_data(204,'The Black Wings of Icarus (Part 2)',[hint('Wind')])).
+assert(episode_name_data(281,'The Small Eye-Witnesses',[hint('Kamen Yaiba\'s special move / TV show')])).
+assert(episode_name_data(280,'Hooligan\'s Labyrinth (Part 2)',[hint('Uniform / Goal scene')])).
+assert(episode_name_data(282,'The Mystery of the Water-Flowing Stone Garden (Part 1)',[hint('Beautiful rock garden')])).
+assert(episode_name_data(279,'Hooligan\'s Labyrinth (Part 1)',[hint('Ticket / Ticket')])).
+assert(episode_name_data(278,'English Teacher vs. Great Western Detective (Part 2)',[hint('Electro-magnetic wave')])).
+assert(episode_name_data(277,'English Teacher vs. Great Western Detective (Part 1)',[hint('Curtain')])).
+assert(episode_name_data(276,'The Policeman\'s Missing Notebook Case',[hint('All-you-can-eat')])).
+assert(episode_name_data(275,'The Truth of the Haunted House (Part 2)',[hint('Beaker')])).
+assert(episode_name_data(274,'The Truth of the Haunted House (Part 1)',[hint('TV screen')])).
+assert(episode_name_data(283,'The Mystery of the Water-Flowing Stone Garden (Part 2)',[hint('Draining of the water')])).
+assert(episode_name_data(284,'Chinatown Deja Vu in the Rain (Part 1)',[hint('Circular table')])).
+assert(episode_name_data(285,'Chinatown Deja Vu in the Rain (Part 2)',[hint('Wet towel')])).
+assert(episode_name_data(286,'Shinichi Kudo\'s New York Case (The Case)',[hint('Costumes / Mirror')])).
+assert(episode_name_data(287,'Shinichi Kudo\'s New York Case (The Deduction)',[hint('Feather')])).
+assert(episode_name_data(288,'Shinichi Kudo\'s New York Case (The Resolution)',[hint('Theater basement')])).
+assert(episode_name_data(273,'Riddle Granny Disappearance Case',[hint('Cat / Chinese character')])).
+assert(episode_name_data(289,'Mitsuhiko\'s Mystifying Forest (Part 1)',[hint('Lemon scent')])).
+assert(episode_name_data(290,'Mitsuhiko\'s Mystifying Forest (Part 2)',[hint('Historical names')])).
+assert(episode_name_data(291,'Solitary Island of the Princess and the Dragon King\'s Palace (The Case)',[hint('Note in the sand')])).
+assert(episode_name_data(292,'Solitary Island of the Princess and the Dragon King\'s Palace (The Pursuit)',[hint('Automatically controlled plane')])).
+assert(episode_name_data(293,'Solitary Island of the Princess and the Dragon King\'s Palace (The Resolution)',[hint('Castle')])).
+assert(episode_name_data(294,'The Smash of Determination and Love (Part 1)',[hint('E-mail')])).
+assert(episode_name_data(272,'The Secret Rushed Omission (Part 2)',[hint('Square')])).
+assert(episode_name_data(271,'The Secret Rushed Omission (Part 1)',[hint('â\u0097\u008BÃ\u0097â\u0096³')])).
+assert(episode_name_data(270,'The Forgotten Memento from the Crime (Part 2)',[hint('Sandwich')])).
+assert(episode_name_data(269,'The Forgotten Memento from the Crime (Part 1)',[hint('Wristwatch')])).
+assert(episode_name_data(267,'The Truth Behind Valentine\'s (The Deduction)',[hint('Playing ball')])).
+assert(episode_name_data(268,'The Truth Behind Valentine\'s (The Resolution)',[hint('Completely black photo')])).
+assert(episode_name_data(266,'The Truth Behind Valentine\'s (The Case)',[hint('Chocolate')])).
+assert(episode_name_data(265,'Courtroom Confrontation: Kisaki vs. Kogoro (Part 2)',[hint('Matches')])).
+assert(episode_name_data(264,'Courtroom Confrontation: Kisaki vs. Kogoro (Part 1)',[hint('Cellphone')])).
+assert(episode_name_data(295,'The Smash of Determination and Love (Part 2)',[hint('Awards ceremony')])).
+assert(episode_name_data(296,'Houseboat Fishing Shock',[hint('Carbon make fishing rod')])).
+assert(episode_name_data(297,'Courtroom Confrontation II: Kisaki vs. Kujo (Part 1)',[hint('Decorative clock')])).
+assert(episode_name_data(298,'Courtroom Confrontation II: Kisaki vs. Kujo (Part 2)',[hint('Glasses')])).
+assert(episode_name_data(299,'The Kanmon Strait of Friendship and Murderous Intent (Part 1)',[hint('Eyesight')])).
+assert(episode_name_data(300,'The Kanmon Strait of Friendship and Murderous Intent (Part 2)',[hint('Palm mark')])).
+assert(episode_name_data(301,'Parade of Malice and Saint (Part 1)',[hint('Video camera')])).
+assert(episode_name_data(302,'Parade of Malice and Saint (Part 2)',[hint('Mail truck')])).
+assert(episode_name_data(303,'The Victim Who Came Back',[hint('Recycling')])).
+assert(episode_name_data(304,'The Trembling Police Headquarters: 12 Million Hostages (2 Hour Special)',[hint('Map symbol')])).
+assert(episode_name_data(305,'The Unseen Suspect (Part 1)',[hint('Door glass')])).
+assert(episode_name_data(306,'The Unseen Suspect (Part 2)',[hint('Lighter')])).
+assert(episode_name_data(202,'The Tenth Passenger (Part 2)',[hint('Bank book')])).
+assert(episode_name_data(199,'Kogoro Mouri, Suspect (Part 1)',[hint('Chain lock / Necktie')])).
+assert(episode_name_data(263,'The Osaka Double Mystery - The Naniwa Swordsman and Toyotomi\'s Castle (2 Hour Special)',[hint('Umbrella')])).
+assert(episode_name_data(262,'The Fearful Legend of the Snowy Night (Part 2)',[hint('Gash')])).
+assert(episode_name_data(261,'The Fearful Legend of the Snowy Night (Part 1)',[hint('Footprints')])).
+assert(episode_name_data(258,'The Man from Chicago (Part 1)',[hint('Strap / James Black')])).
+assert(episode_name_data(259,'The Man from Chicago (Part 2)',[hint('P and A / Man with long hair')])).
+assert(episode_name_data(260,'The Shaking Restaurant',[hint('Fish scale')])).
+assert(episode_name_data(179,'The Coffee Shop Truck\'s Wild Entrance Case',[hint('Bus schedule')])).
+assert(episode_name_data(198,'The Super Car\'s Trap (Part 2)',[hint('Coal tar')])).
+retractall(episode_name_data(200,_,_),1).
+assert(episode_name_data(200,'Kogoro Mouri, Suspect (Part 2)',[hint('Hayashi rice / Room service')])).
+retractall(episode_name_data(198,_,_),1).
+assert(episode_name_data(198,'The Super Car\'s Trap (Part 2)',[hint('Coal tar')])).
+assert(episode_name_data(197,'The Super Car\'s Trap (Part 1)',[hint('Open window')])).
+assert(episode_name_data(307,'On the Trail of a Silent Witness (Part 1)',[hint('Shogi, Chess, and Go / Board game')])).
+assert(episode_name_data(308,'On the Trail of a Silent Witness (Part 2)',[hint('Go pieces / The rules of Go')])).
+assert(episode_name_data(309,'Contact with the Black Organization (The Negotiation)',[hint('Diary / Coin locker')])).
+assert(episode_name_data(310,'Contact with the Black Organization (The Pursuit)',[hint('System-soft')])).
+assert(episode_name_data(311,'Contact with the Black Organization (Desperation)',[hint('Coin locker')])).
+assert(episode_name_data(312,'Festival Dolls Dyed in the Setting Sun (Part 1)',[hint('The Emperor and doll display')])).
+assert(episode_name_data(313,'Festival Dolls Dyed in the Setting Sun (Part 2)',[hint('Stairs')])).
+assert(episode_name_data(314,'The Scenic Lookout with the Broken Fence',[hint('A Japanese White-Eye\'s nest')])).
+assert(episode_name_data(315,'Place Exposed to the Sun',[hint('Nightingale')])).
+assert(episode_name_data(316,'The Sullied Masked Hero (Part 1)',[hint('Hidden camera')])).
+assert(episode_name_data(317,'The Sullied Masked Hero (Part 2)',[hint('Mask')])).
+assert(episode_name_data(318,'The Lucky Cigar Case (Part 1)',[hint('Brake')])).
+assert(episode_name_data(319,'The Lucky Cigar Case (Part 2)',[hint('Cigar')])).
+retractall(episode_name_data(302,_,_),1).
+assert(episode_name_data(302,'Parade of Malice and Saint (Part 2)',[hint('Mail truck')])).
+retractall(episode_name_data(319,_,_),1).
+assert(episode_name_data(319,'The Lucky Cigar Case (Part 2)',[hint('Cigar')])).
+assert(episode_name_data(320,'The Art of Ninja Alibi Craft',[hint('Answering machine')])).
+assert(episode_name_data(321,'The Kidnapper\'s Disappearing Getaway Car (Part 1)',[hint('Honeybee mark')])).
+assert(episode_name_data(322,'The Kidnapper\'s Disappearing Getaway Car (Part 2)',[hint('PET bottle')])).
+assert(episode_name_data(323,'Heiji Hattori\'s Desperate Situation! (Part 1)',[hint('The number 9')])).
+assert(episode_name_data(324,'Heiji Hattori\'s Desperate Situation! (Part 2)',[hint('Die (object)')])).
+assert(episode_name_data(325,'The Red Horse within the Flames (The Case)',[hint('1ã\u0083»2ã\u0083»3')])).
+assert(episode_name_data(326,'The Red Horse within the Flames (The Investigation)',[hint('Guan Yu')])).
+assert(episode_name_data(327,'The Red Horse within the Flames (The Resolution)',[hint('Key holder')])).
+assert(episode_name_data(328,'The Birthday Wine Mystery',[hint('Wine glass')])).
+assert(episode_name_data(329,'A Friendship That Can\'t Be Bought (Part 1)',[hint('Sunroof')])).
+assert(episode_name_data(330,'A Friendship That Can\'t Be Bought (Part 2)',[hint('Belt')])).
+retractall(episode_name_data(318,_,_),1).
+assert(episode_name_data(318,'The Lucky Cigar Case (Part 1)',[hint('Brake')])).
+assert(episode_name_data(331,'The Suspicious Spicy Curry (Part 1)',[hint('Curry rice')])).
+assert(episode_name_data(333,'The Similar Princesses (Part 1)',[hint('Ran\'s mother and Shinichi\'s mother')])).
+assert(episode_name_data(334,'The Similar Princesses (Part 2)',[hint('Bookmark')])).
+assert(episode_name_data(335,'Secret of the Tohto Film Development Studio (Part 1)',[hint('Messy room')])).
+assert(episode_name_data(332,'The Suspicious Spicy Curry (Part 2)',[hint('Electric cable')])).
+assert(episode_name_data(336,'Secret of the Tohto Film Development Studio (Part 2)',[hint('Fluorescent tape')])).
+assert(episode_name_data(337,'Hidden Circumstances of the Falling Incident',[hint('Receipt')])).
+assert(episode_name_data(338,'The Four Porsches (Part 1)',[hint('Reel')])).
+assert(episode_name_data(339,'The Four Porsches (Part 2)',[hint('Power window')])).
+assert(episode_name_data(340,'Hidden Bathroom Secret (Part 1)',[hint('Haibara\'s older sister Akemi Miyano / Tile')])).
+assert(episode_name_data(341,'Hidden Bathroom Secret (Part 2)',[hint('Toilet fixtures / Haibara\'s mother - Elena Miyano')])).
+assert(episode_name_data(342,'The Bride of Huis ten Bosch (1 Hour Special)',[hint('Wedding ring / N/A')])).
+assert(episode_name_data(343,'The Convenience Store Trap (Part 1)',[hint('Keeping things tidy and in order')])).
+assert(episode_name_data(344,'The Convenience Store Trap (Part 2)',[hint('Convenience store bathroom')])).
+assert(episode_name_data(345,'Head-to-Head Match with the Black Organization: A Dual Mystery on a Full Moon Night (2.5 Hour Special)',[hint('Tarot card')])).
+assert(episode_name_data(346,'Find the Buttock\'s Mark (Part 1)',[hint('Emblem')])).
+assert(episode_name_data(350,'The Forgotten Cell Phone (Part 1)',[hint('Telephone number')])).
+assert(episode_name_data(351,'The Forgotten Cell Phone (Part 2)',[hint('A loud sound')])).
+assert(episode_name_data(359,'Metropolitan Police Detective Love Story 5 (Part 2)',[hint('Sports player')])).
+assert(episode_name_data(364,'The Synchronicity Case (Part 1)',[hint('Twins')])).
+assert(episode_name_data(367,'The Tragedy of the Pier in Plain Sight (Part 2)',[hint('Celebration')])).
+assert(episode_name_data(347,'Find the Buttock\'s Mark (Part 2)',[hint('Key cylinder')])).
+assert(episode_name_data(348,'Love, Ghosts, and World Heritage (Part 1)',[hint('Seal')])).
+assert(episode_name_data(349,'Love, Ghosts, and World Heritage (Part 2)',[hint('Magnifying glass')])).
+assert(episode_name_data(352,'The Fishing Tournament Tragedy (Part 1)',[hint('Track')])).
+assert(episode_name_data(353,'The Fishing Tournament Tragedy (Part 2)',[hint('Latest fishing rod model')])).
+assert(episode_name_data(354,'A Small Client (Part 1)',[hint('Facial mole')])).
+assert(episode_name_data(355,'A Small Client (Part 2)',[hint('Video tapes')])).
+assert(episode_name_data(356,'Kaitou Kid\'s Miraculous Midair Walk (1 Hour Special)',[hint('Wind / Suspension')])).
+assert(episode_name_data(358,'Metropolitan Police Detective Love Story 5 (Part 1)',[hint('Daypack')])).
+assert(episode_name_data(357,'Sweetheart is an Illusion of Spring',[hint('Two-person portion meal')])).
+assert(episode_name_data(361,'Teitan High School\'s Ghost Story (Part 1)',[hint('Deck')])).
+assert(episode_name_data(362,'Teitan High School\'s Ghost Story (Part 2)',[hint('Umbrella')])).
+assert(episode_name_data(365,'The Synchronicity Case (Part 2)',[hint('Kogoro\'s investigation')])).
+assert(episode_name_data(368,'The Candy House the Witch Lives In',[hint('Chocolate cake')])).
+assert(episode_name_data(360,'A Mysterious Spring Beetle',[hint('Mobile phone strap')])).
+assert(episode_name_data(363,'The City Crows',[hint('Lottery ticket')])).
+assert(episode_name_data(366,'The Tragedy of the Pier in Plain Sight (Part 1)',[hint('Fishing vocabulary')])).
+assert(episode_name_data(369,'A Lucky Man\'s Suspense',[hint('Gossip')])).
+assert(episode_name_data(370,'Running Away in a Game',[hint('Nameplate')])).
+assert(episode_name_data(371,'A Course Without Protest (Part 1)',[hint('Tennis ball')])).
+assert(episode_name_data(372,'A Course Without Protest (Part 2)',[hint('Chart')])).
+assert(episode_name_data(373,'Deadly Poisonous Spider Trap',[hint('Telephone')])).
+assert(episode_name_data(374,'A Code of Stars and Tobacco (Part 1)',[hint('Tobacco')])).
+assert(episode_name_data(375,'A Code of Stars and Tobacco (Part 2)',[hint('Initial')])).
+assert(episode_name_data(376,'The Time Limit is 15:00!',[hint('Tent')])).
+assert(episode_name_data(377,'Momotarou Mystery Solving Tour (Part 1)',[hint('Mirror image')])).
+assert(episode_name_data(378,'Momotarou Mystery Solving Tour (Part 2)',[hint('Left-handedness')])).
+assert(episode_name_data(379,'The Case of the Furisode at the Hidden Hot Spring on a Snowy Night (Part 1)',[hint('Open air bath')])).
+assert(episode_name_data(380,'The Case of the Furisode at the Hidden Hot Spring on a Snowy Night (Part 2)',[hint('Obi sash')])).
+assert(episode_name_data(382,'Which One\'s Deduction Show (Part 2)',[hint('Calendar')])).
+assert(episode_name_data(381,'Which One\'s Deduction Show (Part 1)',[hint('4 building blocks')])).
+assert(episode_name_data(383,'Miracle at Koshien Ball Park! The Defiants Face the Dark Demon (2 Hour Special)',[hint('Retired uniform number')])).
+assert(episode_name_data(384,'The Target is Kogoro Mouri',[hint('Service ticket / Jacket')])).
+assert(episode_name_data(385,'The Dissonance of the Stradivarius (Prelude)',[hint('Requiem')])).
+assert(episode_name_data(386,'The Dissonance of the Stradivarius (Interlude)',[hint('Initial')])).
+assert(episode_name_data(387,'The Dissonance of the Stradivarius (Postlude)',[hint('Perfect pitch')])).
+assert(episode_name_data(388,'Kogoro Gets Drunk in Satsuma (Part 1)',[hint('Obsession with sake brewing')])).
+assert(episode_name_data(389,'Kogoro Gets Drunk in Satsuma (Part 2)',[hint('Physical examination')])).
+assert(episode_name_data(390,'Metropolitan Police Detective Love Story 6 (Part 1)',[hint('News')])).
+assert(episode_name_data(392,'The Mysterious Height Difference of 20cm',[hint('Chandelier')])).
+assert(episode_name_data(391,'Metropolitan Police Detective Love Story 6 (Part 2)',[hint('Clock')])).
+assert(episode_name_data(400,'Ran\'s Suspicions',[hint('Car\'s number')])).
+assert(episode_name_data(401,'A Jewel Thief Caught Red-Handed (Part 1)',[hint('Brooch')])).
+assert(episode_name_data(403,'The Mysterious Angel\'s Mansion (Part 1)',[hint('Violent ox')])).
+assert(episode_name_data(402,'A Jewel Thief Caught Red-Handed (Part 2)',[hint('Rain')])).
+assert(episode_name_data(404,'The Mysterious Angel\'s Mansion (Part 2)',[hint('Direction')])).
+assert(episode_name_data(393,'A Kidnapping Case... So It Seems',[hint('Oil')])).
+assert(episode_name_data(394,'Big Adventure in the Eccentric Mansion (The Seal)',[hint('Magatama stone / Magatama')])).
+assert(episode_name_data(395,'Big Adventure in the Eccentric Mansion (The Mechanism)',[hint('Three sacred treasures / Mirror')])).
+assert(episode_name_data(396,'Big Adventure in the Eccentric Mansion (The Resolution)',[hint('Samizu Kichiemon / Sword')])).
+assert(episode_name_data(397,'Hot, Bitter, Sweet Soup',[hint('Flour')])).
+assert(episode_name_data(398,'The Strange Family\'s Request (Part 1)',[hint('Cell phone lock')])).
+assert(episode_name_data(399,'The Strange Family\'s Request (Part 2)',[hint('The event from 12 years ago')])).
+assert(episode_name_data(405,'The Man Who Called for an Ambulance',[hint('Victory sign')])).
+assert(episode_name_data(407,'Conan and Heiji\'s Deduction Magic (The Mansion)',[hint('Narrow corridor')])).
+assert(episode_name_data(406,'Conan and Heiji\'s Deduction Magic (The Trick)',[hint('Sleight of hand info')])).
+assert(episode_name_data(408,'Conan and Heiji\'s Deduction Magic (The Resolution)',[hint('Shadow')])).
+assert(episode_name_data(410,'The Simultaneous Stage Advance and Kidnapping (Part 2)',[hint('Phone call from the criminal')])).
+assert(episode_name_data(409,'The Simultaneous Stage Advance and Kidnapping (Part 1)',[hint('The Tale of the Bamboo-Cutter')])).
+assert(episode_name_data(411,'The Shinto Shrine Torii\'s Surprising Code (Part 1)',[hint('Vertical and horizontal')])).
+assert(episode_name_data(412,'The Shinto Shrine Torii\'s Surprising Code (Part 2)',[hint('Top and bottom')])).
+assert(episode_name_data(413,'The Half Completed Crime Mystery',[hint('The Two of Cups')])).
+assert(episode_name_data(414,'The Detective Boys\' Bluebird Chase',[hint('Soy sauce')])).
+assert(episode_name_data(415,'The Evil Spirit Appears on An Unlucky Day (The Case)',[hint('A habit')])).
+assert(episode_name_data(416,'The Evil Spirit Appears on An Unlucky Day (The Suspicion)',[hint('Fingerprint')])).
+assert(episode_name_data(417,'The Evil Spirit Appears on An Unlucky Day (The Resolution)',[hint('Siblings')])).
+assert(episode_name_data(418,'Home of Beika\'s Grenier',[hint('Nameplate')])).
+assert(episode_name_data(419,'Sword of the Eight-Headed Serpent (Part 1)',[hint('Sight-seeing area')])).
+assert(episode_name_data(420,'Sword of the Eight-Headed Serpent (Part 2)',[hint('Evidence of a lie')])).
+assert(episode_name_data(421,'Gingko-Colored First Love (Part 1)',[hint('Promise from 10 years ago')])).
+assert(episode_name_data(422,'Gingko-Colored First Love (Part 2)',[hint('Blond hair')])).
+assert(episode_name_data(423,'The Detective Boys and the Four Caterpillar Brothers',[hint('Olive leaf')])).
+assert(episode_name_data(424,'The Photo Mail from the Clown',[hint('Stew')])).
+assert(episode_name_data(425,'Black Impact! The Moment the Black Organization Reaches Out (2.5 Hour Special)',[hint('Transmitter and listening device / Kir / Chianti & Korn / Gin / Shuichi Akai')])).
+assert(episode_name_data(426,'Love Letter to Ran',[hint('Fencing stick / Diary')])).
diff --git a/episode_data.pl b/episode_data.pl
new file mode 100644
index 0000000..49dddd0
--- /dev/null
+++ b/episode_data.pl
@@ -0,0 +1,68 @@
+:- module(episode_data, [retract_episode/1,
+ lookup_episode/3,
+ lookup_episode_local/3,
+ lookup_episode_remote/3]).
+
+:- use_module(library(dcg/basics)).
+:- use_module(library(http/http_open)).
+:- use_module(library(sgml)).
+:- use_module(library(xpath)).
+:- use_module(library(persistency)).
+:- use_module(atom_dcg).
+
+:- persistent episode_name_data(episode:integer, name:atom, data:list).
+
+attach :-
+ absolute_file_name('episode_data.db', F, [access(write)]),
+ db_attach(F, []).
+
+detach :-
+ db_detach.
+
+retract_episode(Ep) :-
+ ( episode_name_data(Ep, _, _)
+ -> retractall_episode_name_data(Ep, _, _)
+ ; true
+ ).
+
+padding(Ep) --> { Ep < 10 }, "00".
+padding(Ep) --> { Ep >= 10, Ep < 100 }, "0".
+padding(Ep) --> { Ep >= 100 }.
+
+episode_number(Ep) --> padding(Ep), integer(Ep).
+
+lookup_episode(Ep, Name, Data) :- lookup_episode_local(Ep, Name, Data), !.
+lookup_episode(Ep, Name, Data) :- lookup_episode_remote(Ep, Name, Data).
+
+lookup_episode_local(Ep, Name, Data) :-
+ episode_name_data(Ep, Name, Data).
+
+lookup_episode_remote(Ep, Name, Data) :-
+ catch(http_load_html(
+ 'https://www.detectiveconanworld.com/wiki/Next_Conan%27s_Hint',
+ D),
+ _,
+ fail),
+ xpath(D, //tr, D1),
+ xpath(D1, td(index(1),text), T),
+ atom_phrase(episode_number(Ep), T),
+ xpath(D1, td(index(2),text), Name),
+ xpath(D1, td(index(3),text), Hint),
+ Data = [hint(Hint)],
+ assert_episode_name_data(Ep, Name, Data).
+
+http_load_html(URL, DOM) :-
+ setup_call_cleanup(http_open(URL, In,
+ [ timeout(60)
+ ]),
+ ( dtd(html, DTD),
+ load_structure(stream(In),
+ DOM,
+ [ dtd(DTD),
+ dialect(sgml),
+ shorttag(false),
+ max_errors(-1),
+ syntax_errors(quiet)
+ ])
+ ),
+ close(In)).
diff --git a/local_episodes.pl b/local_episodes.pl
new file mode 100644
index 0000000..283ccb7
--- /dev/null
+++ b/local_episodes.pl
@@ -0,0 +1,31 @@
+:- module(local_episodes, [local_episode//1,
+ episode_file/2,
+ open_episode/1]).
+
+:- use_module(library(dcg/basics)).
+:- use_module(atom_dcg).
+
+local_episode -->
+ local_episode(_).
+local_episode(N) -->
+ string(_), "Detective_Conan_-_", integer(N), string(_).
+local_episode(N) -->
+ string(_), "Detective_Conan_-_",
+ integer(First), "-", integer(Last), string(_),
+ { Second is First + 1, between(Second, Last, N) }.
+
+% Find episode on disk.
+
+episode_file(N, F) :-
+ expand_file_name('C:/Users/John/Nedladdningar/Detective Conan season 1 to season 22 + season 23(incomplete)/*/*.*',
+ F1),
+ ( nonvar(N)
+ -> include(atom_phrase(local_episode(N)), F1, [F|_])
+ ; include(atom_phrase(local_episode), F1, F2),
+ member(F, F2),
+ atom_phrase(local_episode(N), F)
+ ).
+
+open_episode(N) :-
+ episode_file(N, F),
+ win_shell(open, F).
diff --git a/mk.bat b/mk.bat
new file mode 100644
index 0000000..a737559
--- /dev/null
+++ b/mk.bat
@@ -0,0 +1,4 @@
+@REM -ld ld -ld-options,--subsystem,windows ^
+swipl-ld -v -g ^
+-goal true -o build/default/episode_browser win.c ^
+track_episodes.pl local_episodes.pl episode_data.pl \ No newline at end of file
diff --git a/track_episodes.db b/track_episodes.db
new file mode 100644
index 0000000..59c075c
--- /dev/null
+++ b/track_episodes.db
@@ -0,0 +1,212 @@
+created(1643727182.419166).
+assert(episode_watched(274,true)).
+assert(episode_watched(275,true)).
+assert(episode_watched(280,true)).
+assert(episode_watched(279,true)).
+assert(episode_watched(278,true)).
+assert(episode_watched(277,true)).
+assert(episode_watched(273,true)).
+assert(episode_watched(272,true)).
+assert(episode_watched(271,true)).
+assert(episode_watched(270,true)).
+assert(episode_watched(269,true)).
+assert(episode_watched(265,true)).
+assert(episode_watched(264,true)).
+assert(episode_watched(268,true)).
+assert(episode_watched(267,true)).
+assert(episode_watched(266,true)).
+assert(episode_watched(263,true)).
+assert(episode_watched(262,true)).
+assert(episode_watched(261,true)).
+assert(episode_watched(260,true)).
+assert(episode_watched(259,true)).
+assert(episode_watched(276,true)).
+assert(episode_watched(282,true)).
+assert(episode_watched(283,true)).
+retractall(episode_watched(283,true),1).
+assert(episode_watched(283,false)).
+retractall(episode_watched(283,false),1).
+assert(episode_watched(283,true)).
+retractall(episode_watched(283,true),1).
+assert(episode_watched(283,false)).
+retractall(episode_watched(283,false),1).
+assert(episode_watched(283,true)).
+retractall(episode_watched(283,true),1).
+assert(episode_watched(283,false)).
+retractall(episode_watched(283,false),1).
+assert(episode_watched(283,true)).
+retractall(episode_watched(282,true),1).
+assert(episode_watched(282,false)).
+retractall(episode_watched(282,false),1).
+assert(episode_watched(282,true)).
+assert(episode_watched(284,true)).
+retractall(episode_watched(284,true),1).
+assert(episode_watched(284,true)).
+retractall(episode_watched(284,true),1).
+retractall(episode_watched(283,true),1).
+assert(episode_watched(283,true)).
+retractall(episode_watched(282,true),1).
+assert(episode_watched(282,true)).
+retractall(episode_watched(278,true),1).
+assert(episode_watched(278,false)).
+retractall(episode_watched(278,false),1).
+assert(episode_watched(278,true)).
+assert(episode_watched(281,true)).
+assert(episode_watched(284,true)).
+assert(episode_watched(285,true)).
+assert(episode_watched(286,true)).
+assert(episode_watched(287,true)).
+assert(episode_watched(288,true)).
+retractall(episode_watched(288,true),1).
+assert(episode_watched(288,false)).
+retractall(episode_watched(288,false),1).
+assert(episode_watched(288,true)).
+retractall(episode_watched(288,true),1).
+assert(episode_watched(288,true)).
+assert(episode_watched(288,true)).
+assert(episode_watched(287,true)).
+assert(episode_watched(286,true)).
+assert(episode_watched(285,true)).
+assert(episode_watched(284,true)).
+assert(episode_watched(281,true)).
+assert(episode_watched(289,true)).
+assert(episode_watched(290,true)).
+assert(episode_watched(291,true)).
+retractall(episode_watched(291,true),1).
+assert(episode_watched(291,true)).
+retractall(episode_watched(291,true),1).
+assert(episode_watched(291,false)).
+retractall(episode_watched(291,false),1).
+assert(episode_watched(291,true)).
+retractall(episode_watched(291,true),1).
+retractall(episode_watched(290,true),1).
+assert(episode_watched(290,false)).
+retractall(episode_watched(290,false),1).
+assert(episode_watched(290,true)).
+assert(episode_watched(258,true)).
+assert(episode_watched(291,true)).
+assert(episode_watched(292,true)).
+retractall(episode_watched(292,true),1).
+assert(episode_watched(292,true)).
+assert(episode_watched(293,true)).
+assert(episode_watched(294,true)).
+assert(episode_watched(295,true)).
+assert(episode_watched(299,true)).
+assert(episode_watched(300,true)).
+assert(episode_watched(301,true)).
+assert(episode_watched(302,true)).
+retractall(episode_watched(302,true),1).
+assert(episode_watched(302,false)).
+retractall(episode_watched(302,false),1).
+assert(episode_watched(302,true)).
+assert(episode_watched(303,true)).
+retractall(episode_watched(303,true),1).
+assert(episode_watched(304,true)).
+assert(episode_watched(305,true)).
+assert(episode_watched(306,true)).
+assert(episode_watched(307,true)).
+assert(episode_watched(308,true)).
+assert(episode_watched(309,true)).
+assert(episode_watched(310,true)).
+assert(episode_watched(311,true)).
+assert(episode_watched(312,true)).
+assert(episode_watched(313,true)).
+assert(episode_watched(314,true)).
+assert(episode_watched(316,true)).
+assert(episode_watched(317,true)).
+assert(episode_watched(303,true)).
+assert(episode_watched(315,true)).
+assert(episode_watched(318,true)).
+retractall(episode_watched(318,true),1).
+assert(episode_watched(318,false)).
+retractall(episode_watched(318,false),1).
+assert(episode_watched(318,true)).
+retractall(episode_watched(318,true),1).
+assert(episode_watched(318,false)).
+retractall(episode_watched(318,false),1).
+assert(episode_watched(318,true)).
+retractall(episode_watched(318,true),1).
+assert(episode_watched(318,false)).
+retractall(episode_watched(317,true),1).
+assert(episode_watched(317,false)).
+retractall(episode_watched(317,false),1).
+assert(episode_watched(317,true)).
+retractall(episode_watched(318,false),1).
+assert(episode_watched(318,true)).
+retractall(episode_watched(318,true),1).
+assert(episode_watched(318,false)).
+retractall(episode_watched(317,true),1).
+assert(episode_watched(317,true)).
+retractall(episode_watched(318,false),1).
+assert(episode_watched(318,true)).
+assert(episode_watched(319,true)).
+assert(episode_watched(320,true)).
+assert(episode_watched(321,true)).
+assert(episode_watched(322,true)).
+assert(episode_watched(323,true)).
+assert(episode_watched(324,true)).
+assert(episode_watched(325,true)).
+assert(episode_watched(326,true)).
+assert(episode_watched(327,true)).
+assert(episode_watched(329,true)).
+assert(episode_watched(330,true)).
+assert(episode_watched(331,true)).
+assert(episode_watched(332,true)).
+assert(episode_watched(333,true)).
+assert(episode_watched(334,true)).
+assert(episode_watched(335,true)).
+assert(episode_watched(336,true)).
+assert(episode_watched(338,true)).
+assert(episode_watched(339,true)).
+assert(episode_watched(340,true)).
+assert(episode_watched(341,true)).
+assert(episode_watched(342,true)).
+assert(episode_watched(343,true)).
+assert(episode_watched(345,true)).
+assert(episode_watched(344,true)).
+assert(episode_watched(346,true)).
+assert(episode_watched(347,true)).
+assert(episode_watched(348,true)).
+assert(episode_watched(349,true)).
+assert(episode_watched(350,true)).
+assert(episode_watched(351,true)).
+assert(episode_watched(352,true)).
+assert(episode_watched(353,true)).
+assert(episode_watched(354,true)).
+assert(episode_watched(355,true)).
+assert(episode_watched(356,true)).
+assert(episode_watched(357,true)).
+assert(episode_watched(358,true)).
+assert(episode_watched(359,true)).
+assert(episode_watched(361,true)).
+assert(episode_watched(362,true)).
+assert(episode_watched(364,true)).
+assert(episode_watched(365,true)).
+assert(episode_watched(366,true)).
+assert(episode_watched(367,true)).
+assert(episode_watched(371,true)).
+assert(episode_watched(372,true)).
+assert(episode_watched(374,true)).
+assert(episode_watched(375,true)).
+assert(episode_watched(376,true)).
+assert(episode_watched(381,true)).
+assert(episode_watched(382,true)).
+assert(episode_watched(383,true)).
+assert(episode_watched(385,true)).
+assert(episode_watched(386,true)).
+assert(episode_watched(387,true)).
+assert(episode_watched(390,true)).
+assert(episode_watched(391,true)).
+assert(episode_watched(394,true)).
+assert(episode_watched(395,true)).
+assert(episode_watched(396,true)).
+assert(episode_watched(398,true)).
+assert(episode_watched(399,true)).
+assert(episode_watched(400,true)).
+assert(episode_watched(401,true)).
+assert(episode_watched(402,true)).
+assert(episode_watched(328,true)).
+assert(episode_watched(360,true)).
+assert(episode_watched(379,true)).
+assert(episode_watched(380,true)).
+assert(episode_watched(296,true)).
diff --git a/track_episodes.pl b/track_episodes.pl
new file mode 100644
index 0000000..ec1785b
--- /dev/null
+++ b/track_episodes.pl
@@ -0,0 +1,96 @@
+:- module(track_episodes, [update_tracked_episodes/0,
+ toggle_episode/1,
+ forget_episode/1,
+ most_recently_watched/1,
+ watched/1]).
+
+:- use_module(library(dcg/basics)).
+:- use_module(library(registry)).
+:- use_module(library(persistency)).
+:- use_module(local_episodes).
+
+attach :-
+ absolute_file_name('track_episodes.db', F, [access(write)]),
+ db_attach(F, []).
+
+detach :-
+ db_detach.
+
+:- persistent episode_watched(episode:integer, watched:atom).
+
+% Helpers.
+
+swapbc(P, A, B, C, D) :- call(P, A, C, B, D).
+compound(F, A, B, T) :- T =.. [F, A, B].
+
+% Get data from registry.
+
+file(N) --> "File Name ", integer(N).
+
+pos(N) --> "File Position ", integer(N).
+
+mpc_reg(Phrase, Value) :-
+ phrase(Phrase, Name),
+ atom_codes(Name1, Name),
+ registry_get_key(current_user/software/'mpc-hc'/'mpc-hc'/settings,
+ Name1, Value).
+
+% Create episode-position list.
+
+epipos(L) :- epipos_x(0, L).
+
+epipos_x(N, L) :-
+ ( catch((mpc_reg(file(N), File),
+ mpc_reg(pos(N), Pos)),
+ _,
+ fail)
+ -> M is N + 1,
+ ( atom_codes(File, File1),
+ findall(Ep, phrase(local_episode(Ep), File1), Eps)
+ -> atom_codes(Pos, Pos1),
+ phrase(integer(Pos2), Pos1),
+ maplist(swapbc(compound, '-', Pos2), Eps, Eps1),
+ append(Eps1, T, L),
+ epipos_x(M, T)
+ ; epipos_x(M, L))
+ ; L = []
+ ).
+
+considered_watched(_-Pos) :-
+ Pos > 11000000000.
+
+% Update database.
+
+update_tracked_episodes :-
+ epipos(All),
+ include(considered_watched, All, Watched),
+ maplist(add, Watched).
+
+add(Ep-_) :- episode_watched(Ep, _), !.
+add(Ep-_) :- assert_episode_watched(Ep, true).
+
+forget_episode(Ep) :-
+ retractall_episode_watched(Ep, true),
+ retractall_episode_watched(Ep, false).
+
+toggle_episode(Ep) :-
+ episode_watched(Ep, true), !,
+ retractall_episode_watched(Ep, true),
+ assert_episode_watched(Ep, false).
+
+toggle_episode(Ep) :-
+ episode_watched(Ep, false), !,
+ retractall_episode_watched(Ep, false),
+ assert_episode_watched(Ep, true).
+
+toggle_episode(Ep) :-
+ assert_episode_watched(Ep, true).
+
+% Utility.
+
+most_recently_watched(Ep) :-
+ findall(N, episode_watched(N, true), All),
+ sort(0, @>, All, [Ep|_]).
+
+watched(Ep) :-
+ episode_watched(Ep, true).
diff --git a/win.c b/win.c
new file mode 100644
index 0000000..660275d
--- /dev/null
+++ b/win.c
@@ -0,0 +1,122 @@
+#include <windows.h>
+#include <SWI-Prolog.h>
+#include <stdio.h>
+
+static int Attach(void);
+static void ShowEpisode(int);
+static void UpdateList(void);
+
+/*
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ PSTR lpCmdLine, INT nCmdShow)
+*/
+int main(int argc, char *argv[])
+{
+ char *rgArgs[2];
+
+ rgArgs[0] = "episode_browser";
+ rgArgs[1] = NULL;
+
+ if (!PL_initialise(1, rgArgs))
+ PL_halt(1);
+
+ Attach();
+ //UpdateList();
+ ShowEpisode(400);
+
+ PL_halt(0);
+}
+
+/* Attach persistent databases. */
+int Attach()
+{
+ int r;
+ term_t t;
+
+ t = PL_new_term_refs(2);
+ r = PL_call_predicate(NULL, PL_Q_NORMAL,
+ PL_predicate("attach", 0, "track_episodes"),
+ t);
+ if (!r) return r;
+
+ r = PL_call_predicate(NULL, PL_Q_NORMAL,
+ PL_predicate("attach", 0, "episode_data"),
+ t);
+ if (!r) return r;
+
+ return 1;
+}
+
+/* Update episode list. */
+void UpdateList()
+{
+ term_t t;
+ qid_t q;
+
+ t = PL_new_term_refs(2);
+ PL_call_predicate(NULL, PL_Q_NORMAL,
+ PL_predicate("update_tracked_episodes", 0, "track_episode"),
+ t);
+
+ q = PL_open_query(NULL, PL_Q_NORMAL,
+ PL_predicate("episode_file", 2, "local_episodes"),
+ t);
+
+ while (PL_next_solution(q)) {
+ char *sFile;
+ int nEpisode;
+
+ if (!PL_get_integer(t+0, &nEpisode))
+ goto close;
+ if (!PL_get_atom_chars(t+1, &sFile))
+ goto close;
+ printf("%d: %s\n", nEpisode, sFile);
+ }
+
+close:
+ PL_close_query(q); /* Or PL_cut_query? */
+}
+
+/* Show episode data. */
+void ShowEpisode(int nEpisode)
+{
+ int r;
+ term_t t;
+
+ t = PL_new_term_refs(3);
+ if(!PL_put_integer(t+0, nEpisode))
+ return;
+
+ r = PL_call_predicate(NULL, PL_Q_NORMAL,
+ PL_predicate("lookup_episode_local", 3, "episode_data"),
+ t);
+ if (!r) return;
+
+ /* The episode data is a list of unary compounds,
+ * whose name is the key and whose value is the value. */
+
+ {
+ term_t tHead, tList;
+
+ tHead = PL_new_term_ref();
+ tList = PL_copy_term_ref(t+2);
+
+ while (PL_get_list(tList, tHead, tList)) {
+ atom_t aKey;
+ const char *sKey;
+ char *sValue;
+ term_t tValue;
+ size_t iArity;
+
+ if (!PL_get_name_arity(tHead, &aKey, &iArity))
+ continue;
+ if (!PL_get_arg(1, tHead, tValue))
+ continue;
+ sKey = PL_atom_chars(aKey);
+ if(!PL_get_atom_chars(tValue, &sValue))
+ continue;
+
+ printf("%s/%d: %s\n", sKey, iArity, sValue);
+ }
+ }
+}