code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using RimWorld;
using rjw.Modules.Interactions.Helpers;
using rjw.Modules.Interactions.Objects;
using UnityEngine;
using Verse;
using Verse.AI;
using rjw.Modules.Interactions.Enums;

namespace Rimworld_Animations {
    public static class AnimationUtility {
        /*
         Note: always make the list in this order:
             Female pawns, animal female pawns, male pawns, animal male pawns
        */
        public static AnimationDef tryFindAnimation(ref List<Pawn> participants, rjw.xxx.rjwSextype sexType = 0, rjw.SexProps sexProps = null) {

            InteractionWithExtension interaction = InteractionHelper.GetWithExtension(sexProps.dictionaryKey);

            if(interaction.HasInteractionTag(InteractionTag.Reverse))
            {
                Pawn buffer = participants[1];
                participants[1] = participants[0];
                participants[0] = buffer;
            }

            participants =
                participants.OrderBy(p => p.jobs.curDriver is rjw.JobDriver_SexBaseInitiator)
                .OrderBy(p => rjw.xxx.can_fuck(p))
                .ToList();

            List<Pawn> localParticipants = new List<Pawn>(participants);

            IEnumerable<AnimationDef> options = DefDatabase<AnimationDef>.AllDefs.Where((AnimationDef x) => {

                if (x.actors.Count != localParticipants.Count) {
                    return false;
                }
                for (int i = 0; i < x.actors.Count; i++) {

                    if (rjw.RJWPreferenceSettings.Malesex == rjw.RJWPreferenceSettings.AllowedSex.Nohomo) {
                        if (rjw.xxx.is_male(localParticipants[i]) && x.actors[i].isFucked) {
                            return false;
                        }
                }
				if (x.actors[i].requiredGender != null && !x.actors[i].requiredGender.Contains(localParticipants[i].gender.ToStringSafe<Gender>()))
				{
					if (AnimationSettings.debugMode)
					{
						Log.Message(string.Concat(new string[]
						{
							x.defName.ToStringSafe<string>(),
							" not selected -- ",
							localParticipants[i].def.defName.ToStringSafe<string>(),
							" ",
							localParticipants[i].Name.ToStringSafe<Name>(),
							" does not match required gender"
						}));
					}
					return false;
				}
                    if ((x.actors[i].blacklistedRaces != null) && x.actors[i].blacklistedRaces.Contains(localParticipants[i].def.defName)) {
                        if(AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is blacklisted");
                        return false;
                    }

                    if(x.actors[i].defNames.Contains("Human")) {
                        if (!rjw.xxx.is_human(localParticipants[i])) {
                            if (AnimationSettings.debugMode)
                                Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not human");

                            return false;
                        }

                    }
                    else if (!x.actors[i].bodyDefTypes.Contains(localParticipants[i].RaceProps.body)) {

                        if (!x.actors[i].defNames.Contains(localParticipants[i].def.defName)) {

                            if (rjw.RJWSettings.DevMode) {
                                string animInfo = x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " is not ";
                                foreach(String defname in x.actors[i].defNames) {
                                    animInfo += defname + ", ";
                                }
                                if (AnimationSettings.debugMode)
                                    Log.Message(animInfo);
                            }

                            return false;
                        }
                    }
                    //genitals checking

                    if(!GenitalCheckForPawn(x.actors[i].requiredGenitals, localParticipants[i], out string failReason)) {
                        Debug.Log("Didn't select " + x.defName + ", " + localParticipants[i].Name + " " + failReason);
                        return false;
                    }

                    //TESTING ANIMATIONS ONLY REMEMBER TO COMMENT OUT BEFORE PUSH
                    /*
                    if (x.defName != "Cunnilingus")
                        return false;
                    */

                    if (x.actors[i].isFucking && !rjw.xxx.can_fuck(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't fuck");
                        return false;
                    }

                    if (x.actors[i].isFucked && !rjw.xxx.can_be_fucked(localParticipants[i])) {
                        if (AnimationSettings.debugMode)
                            Log.Message(x.defName.ToStringSafe() + " not selected -- " + localParticipants[i].def.defName.ToStringSafe() + " " + localParticipants[i].Name.ToStringSafe() + " can't be fucked");
                        return false;
                    }
                }
                return true;
            });
            List<AnimationDef> optionsWithInteractionType = options.ToList().FindAll(x => x.interactionDefTypes != null && x.interactionDefTypes.Contains(sexProps.sexType.ToStringSafe()));
            if (optionsWithInteractionType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for interaction type " + sexProps.sexType.ToStringSafe() + "...");
                return optionsWithInteractionType.RandomElement();
            }
            List<AnimationDef> optionsWithSexType = options.ToList().FindAll(x => x.sexTypes != null && x.sexTypes.Contains(sexType));
            if (optionsWithSexType.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for rjwSexType " + sexType.ToStringSafe() + "...");
                return optionsWithSexType.RandomElement();
            }

            /*
            if(optionsWithInitiator.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Selecting animation for initiators...");
            }
            */

            if (options != null && options.Any()) {
                if (AnimationSettings.debugMode)
                    Log.Message("Randomly selecting animation...");
                return options.RandomElement();
            }
            else
                return null;
        }

        public static void RenderPawnHeadMeshInAnimation1(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool drawNow, Pawn pawn) {

            if (pawn == null || pawn.Map != Find.CurrentMap) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, drawNow);
            } else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(MeshPool.humanlikeHeadSet.MeshAt(pawnAnimator.headFacing), pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, true);
            }
        }

        public static void AdjustHead(ref Quaternion quat, ref Rot4 bodyFacing, ref Vector3 pos, ref float angle, Pawn pawn, PawnRenderFlags flags)
        {
            if (flags.FlagSet(PawnRenderFlags.Portrait)) return;

            CompBodyAnimator anim = pawn.TryGetComp<CompBodyAnimator>();
            if (anim.isAnimating)
            {
                bodyFacing = anim.headFacing;
                angle = anim.headAngle;
                quat = Quaternion.AngleAxis(anim.headAngle, Vector3.up);
                pos = anim.getPawnHeadOffset();

            }
        }

        public static void RenderPawnHeadMeshInAnimation(Mesh mesh, Vector3 loc, Quaternion quaternion, Material material, bool portrait, Pawn pawn, float bodySizeFactor = 1) {

            if (pawn == null) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
                return;
            }

            CompBodyAnimator pawnAnimator = pawn.TryGetComp<CompBodyAnimator>();

            if (pawnAnimator == null || !pawnAnimator.isAnimating || portrait) {
                GenDraw.DrawMeshNowOrLater(mesh, loc, quaternion, material, portrait);
            }
            else {
                Vector3 pawnHeadPosition = pawnAnimator.getPawnHeadPosition();
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.x *= bodySizeFactor;
                pawnHeadPosition.y = loc.y;
                GenDraw.DrawMeshNowOrLater(mesh, pawnHeadPosition, Quaternion.AngleAxis(pawnAnimator.headAngle, Vector3.up), material, portrait);
            }
        }

        public static bool GenitalCheckForPawn(List<string> requiredGenitals, Pawn pawn, out string failReason) {

            failReason = null;
            if (requiredGenitals != null) {
                if (requiredGenitals.Contains("Vagina")) {

                    if (!rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "missing vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Penis")) {

                    if (!(rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn) || rjw.Genital_Helper.has_ovipositorM(pawn) || rjw.Genital_Helper.has_ovipositorF(pawn))) {
                        failReason = "missing penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Mouth")) {

                    if (!rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "missing mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Anus")) {

                    if (!rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "missing anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("Breasts")) {
                    if (!rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "missing breasts";
                        return false;
                    }
                }

                if (requiredGenitals.Contains("NoVagina")) {

                    if (rjw.Genital_Helper.has_vagina(pawn)) {
                        failReason = "has vagina";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoPenis")) {

                    if ((rjw.Genital_Helper.has_multipenis(pawn) || rjw.Genital_Helper.has_penis_infertile(pawn) || rjw.Genital_Helper.has_penis_fertile(pawn))) {
                        failReason = "has penis";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoMouth")) {

                    if (rjw.Genital_Helper.has_mouth(pawn)) {
                        failReason = "has mouth";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoAnus")) {

                    if (rjw.Genital_Helper.has_anus(pawn)) {
                        failReason = "has anus";
                        return false;
                    }

                }

                if (requiredGenitals.Contains("NoBreasts")) {
                    if (rjw.Genital_Helper.can_do_breastjob(pawn)) {
                        failReason = "has breasts";
                        return false;
                    }
                }
            }

            return true;

        }

        public static Rot4 PawnHeadRotInAnimation(Pawn pawn, Rot4 regularPos)
        {
            Debug.Log("Test");

            if(pawn?.TryGetComp<CompBodyAnimator>() != null && pawn.TryGetComp<CompBodyAnimator>().isAnimating)
            {
                return pawn.TryGetComp<CompBodyAnimator>().headFacing;
            }

            return regularPos;
        }
    }
}


code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using Verse;

namespace Rimworld_Animations
{
    public class PatchOperationAddOrReplace : PatchOperationPathed
    {

        protected string key;
        private XmlContainer value;

        protected override bool ApplyWorker(XmlDocument xml)
        {
            XmlNode valNode = value.node;
            bool result = false;
            IEnumerator enumerator = xml.SelectNodes(xpath).GetEnumerator();
            try
            {
                while (enumerator.MoveNext())
                {
                    object obj = enumerator.Current;
                    result = true;
                    XmlNode parentNode = obj as XmlNode;
                    XmlNode xmlNode = parentNode.SelectSingleNode(key);
                    if (xmlNode == null)
                    {
                        // Add - Add node if not existing
                        xmlNode = parentNode.OwnerDocument.CreateElement(key);
                        parentNode.AppendChild(xmlNode);
                    }
                    else
                    {
                        // Replace - Clear existing children
                        xmlNode.RemoveAll();
                    }
                    // (Re)add value
                    xmlNode.AppendChild(parentNode.OwnerDocument.ImportNode(valNode.FirstChild, true));
                }
            }
            finally
            {
                IDisposable disposable = enumerator as IDisposable;
                if (disposable != null)
                {
                    disposable.Dispose();
                }
            }
            return result;
        }

    }

}

repo_name: sdsaaa/rimworld

license: unknown


code:
<?xml version="1.0" encoding="utf-8"?>

<ModMetaData>
	<name>Rimworld-Animations</name>
	<author>C0ffee</author>
  <url>https://gitgud.io/c0ffeeeeeeee/rimworld-animations</url>
	<supportedVersions>

1.1

1.2

1.3

	</supportedVersions>
	<packageId>c0ffee.rimworld.animations</packageId>
	<modDependencies>

			<packageId>brrainz.harmony</packageId>
			<displayName>Harmony</displayName>
			<steamWorkshopUrl>steam://url/CommunityFilePage/2009463077</steamWorkshopUrl>
			<downloadUrl>https://github.com/pardeike/HarmonyRimWorld/releases/latest</downloadUrl>

			<packageId>UnlimitedHugs.HugsLib</packageId>
			<displayName>HugsLib</displayName>
			<downloadUrl>https://github.com/UnlimitedHugs/RimworldHugsLib/releases/latest</downloadUrl>
			<steamWorkshopUrl>steam://url/CommunityFilePage/818773962</steamWorkshopUrl>

			<packageId>rim.job.world</packageId>
			<displayName>RimJobWorld</displayName>
			<downloadUrl>https://www.loverslab.com/topic/110270-mod-rimjobworld/</downloadUrl>

			<packageId>erdelf.HumanoidAlienRaces</packageId>
			<displayName>Humanoid Alien Races 2.0</displayName>
			<steamWorkshopUrl>https://steamcommunity.com/sharedfiles/filedetails/?id=839005762</steamWorkshopUrl>

	</modDependencies>
	<loadAfter>

UnlimitedHugs.HugsLib

brrainz.harmony

erdelf.humanoidalienraces

nals.facialanimation

com.yayo.yayoAni

	</loadAfter>
	<loadBefore>
	</loadBefore>
	<incompatibleWith>

velc.HatsDisplaySelection

	</incompatibleWith>
	<description>
    Rimworld Animations! Hurray!

    Questions or bugs?
    Chat with me on the forums: https://www.loverslab.com/topic/140386-rjw-animations/
    Or on the rjw discord: https://discord.gg/CXwHhv8

  </description>
</ModMetaData>


code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Manifest>
  <identifier>Rimworld-Animations</identifier>
  <version>1.2.5</version>
</Manifest>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <OffsetManager.label>gerenciador de posições</OffsetManager.label>
  <OffsetManager.description>Controla as posições sexuais dos colonos.</OffsetManager.description>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Dog_Doggystyle.label>estilo cachorrinho</Dog_Doggystyle.label>
  <Horse_Cowgirl.label>vaqueira</Horse_Cowgirl.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Tribadism.label>tribadismo</Tribadism.label>
  <Cunnilingus.label>cunilíngua</Cunnilingus.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Double_Penetration.label>dupla penetração</Double_Penetration.label>

</LanguageData>


code:
<?xml version="1.0" encoding="utf-8"?>
<LanguageData>

  <Doggystyle.label>estilo cachorrinho</Doggystyle.label>
  <Blowjob.label>boquete</Blowjob.label>
  <ReverseStandAndCarry.label>69</ReverseStandAndCarry.label>
  <Cowgirl.label>vaqueira</Cowgirl.label>

</LanguageData>


code:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<loadFolders>
	<v1.1>

/

1.1

	</v1.1>
	<v1.2>

/

1.2

Patch_HatsDisplaySelection/1.2

	</v1.2>
	<v1.3>

/

1.3

Patch_HatsDisplaySelection/1.2

Patch_SexToysMasturbation

Patch_SexToysMasturbation/1.3

	</v1.3>

</loadFolders>


code:
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
	<Patch_SexToysMasturbation.SexToyAnimationDef>
		<defName>Masturbation_DildoVaginal</defName>
		dildo masturbation
		<requiredBodyPart>vagina</requiredBodyPart>
		<sounds>true</sounds>
		<sexTypes>

Masturbation

		</sexTypes>
		<actors>

				<defNames>

Human

				</defNames>
				<isFucked>true</isFucked>

		</actors>

		<animationStages>

				<stageName>Masturbating</stageName>
				<isLooping>true</isLooping>
				<playTimeTicks>917</playTimeTicks>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>40</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>30</tickDuration>
								<bodyAngle>76.4867554</bodyAngle>
								<headAngle>45.3887634</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0506898165</bodyOffsetZ>
								<bodyOffsetX>0.08564949</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>30</tickDuration>
								<bodyAngle>78.22131</bodyAngle>
								<headAngle>48.0072327</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.039129138</bodyOffsetZ>
								<bodyOffsetX>0.07794231</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>30</tickDuration>
								<bodyAngle>76.4867554</bodyAngle>
								<headAngle>45.3887634</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0506898165</bodyOffsetZ>
								<bodyOffsetX>0.08564949</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>40</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.2783391</positionX>
								<positionZ>-0.0514066666</positionZ>
								<rotation>81.16443</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.1704393</positionX>
								<positionZ>-0.0668209046</positionZ>
								<rotation>72.8611145</rotation>

								<tickDuration>30</tickDuration>
								<positionX>-0.2783391</positionX>
								<positionZ>-0.0514066666</positionZ>
								<rotation>81.16443</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

						</keyframes>

				</animationClips>

				<stageName>GettingIntoPosition</stageName>
				<isLooping>false</isLooping>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>50</tickDuration>
								<bodyAngle>73.01611</bodyAngle>
								<headAngle>40.0739746</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.054543376</bodyOffsetZ>
								<bodyOffsetX>0.112624526</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>50</tickDuration>
								<positionX>-0.359264076</positionX>
								<positionZ>-0.00901746</positionZ>
								<rotation>114.011215</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>FastMasturbation</stageName>
				<isLooping>true</isLooping>
				<stageIndex>0</stageIndex>
				<playTimeTicks>1610</playTimeTicks>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>20</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>25</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>25</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>20</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>VeryFastMasturbation</stageName>
				<isLooping>true</isLooping>
				<stageIndex>0</stageIndex>
				<playTimeTicks>225</playTimeTicks>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<tickDuration>6</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>8</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>6</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>8</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

				<stageName>Orgasm</stageName>
				<isLooping>false</isLooping>
				<stageIndex>0</stageIndex>
				<animationClips>

						<layer>LayingPawn</layer>
						<keyframes>

								<soundEffect>Slimy</soundEffect>
								<tickDuration>6</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>80</tickDuration>
								<bodyAngle>85.17255</bodyAngle>
								<headAngle>58.0615845</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03527552</bodyOffsetZ>
								<bodyOffsetX>0.0471138731</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>90</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<quiver>True</quiver>
								<soundEffect>Cum</soundEffect>
								<tickDuration>70</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>70</tickDuration>
								<bodyAngle>92.15109</bodyAngle>
								<headAngle>96.34238</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.0237147212</bodyOffsetZ>
								<bodyOffsetX>0.0432603136</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

								<tickDuration>1</tickDuration>
								<bodyAngle>81.65927</bodyAngle>
								<headAngle>58.8843079</headAngle>
								<genitalAngle>0</genitalAngle>
								<bodyOffsetZ>0.03912908</bodyOffsetZ>
								<bodyOffsetX>0.08950315</bodyOffsetX>
								<headBob>0</headBob>
								<bodyFacing>3</bodyFacing>
								<headFacing>3</headFacing>

						</keyframes>

						<keyframes>

								<tickDuration>6</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

								<tickDuration>80</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>90</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>70</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>70</tickDuration>
								<positionX>-0.178146541</positionX>
								<positionZ>-0.01672452</positionZ>
								<rotation>96.95889</rotation>

								<tickDuration>1</tickDuration>
								<positionX>-0.2899</positionX>
								<positionZ>-0.0282852575</positionZ>
								<rotation>98.13748</rotation>

						</keyframes>

				</animationClips>

		</animationStages>
	</Patch_SexToysMasturbation.SexToyAnimationDef>
</Defs>


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{87763712-0536-4D5F-9EAA-520F15D4F84E}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Patch_SexToysMasturbation</RootNamespace>
    <AssemblyName>Patch_SexToysMasturbation</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>1.3\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>pdbonly</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\..\workshop\content\294100\1127530465\1.3\Assemblies\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Assembly-CSharp">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Rimworld-Animations, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
      <SpecificVersion>False</SpecificVersion>
      <HintPath>..\1.3\Assemblies\Rimworld-Animations.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\..\rjw\1.3\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW-ToysAndMasturbation">
      <HintPath>..\..\rjw-toys-and-masturbation\Assemblies\RJW-ToysAndMasturbation.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>..\..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="Source\Defs\SexToyAnimationDef.cs" />
    <Compile Include="Source\Patches\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="Source\Harmony\Harmony_PatchAll.cs" />
    <Compile Include="Source\Utilities\AnimSexToyUtility.cs" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="1.3\Assemblies\" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.3\Defs\AnimationDefs\Animations_Dildo.xml" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Patch_SexToysMasturbation")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Patch_SexToysMasturbation")]
[assembly: AssemblyCopyright("Copyright ©  2022")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("87763712-0536-4d5f-9eaa-520f15d4f84e")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


code:
using Rimworld_Animations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Patch_SexToysMasturbation
{
    public class SexToyAnimationDef : AnimationDef
    {

        public String requiredBodyPart = null;

    }
}


code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;
using HarmonyLib;
using System.Reflection;

namespace Patch_SexToysMasturbation
{

    [StaticConstructorOnStartup]
    public static class Harmony_PatchAll {

        static Harmony_PatchAll() {

            Harmony val = new Harmony("animtoyspatch");
            val.PatchAll(Assembly.GetExecutingAssembly());

        }
    }
}


code:
using HarmonyLib;
using Rimworld_Animations;
using rjw;
using RJW_ToysAndMasturbation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Patch_SexToysMasturbation
{
    [HarmonyPatch(typeof(JobDriver_SexBaseInitiator), "Start")]
    public class HarmonyPatch_JobDriver_SexBaseInitiator
    {

        public static void Postfix(ref JobDriver_SexBaseInitiator __instance)
        {

            if(__instance is JobDriver_MasturbateWithToy masturbateJobDriver)
            {

                Log.Message("Rerolling animations...");
                Pawn pawn = masturbateJobDriver.pawn;
                Thing sexToy = masturbateJobDriver.dildo;

                RerollAnimationsForSexToy(pawn, sexToy, masturbateJobDriver.Bed);
            }

        }

        public static void RerollAnimationsForSexToy(Pawn pawn, Thing thing, Thing bed)
        {
            CompSexToy sextoy = thing.TryGetComp<CompSexToy>();

            SexToyAnimationDef anim = AnimSexToyUtility.tryFindAnimation(sextoy, pawn);

            if (anim != null)
            {
                Log.Message("Playing anim " + anim.defName);

                if(bed != null)
                {
                    pawn.TryGetComp<CompBodyAnimator>().setAnchor(bed);
                    thing.TryGetComp<CompThingAnimator>().setAnchor(bed);
                }
                else
                {
                    pawn.TryGetComp<CompBodyAnimator>().setAnchor(pawn.Position);
                    thing.TryGetComp<CompThingAnimator>().setAnchor(pawn.Position);
                }

                bool mirror = GenTicks.TicksGame % 2 == 0;

                pawn.TryGetComp<CompBodyAnimator>().StartAnimation(anim, new List<Pawn> { pawn }, 0, mirror);
                thing.TryGetComp<CompThingAnimator>().StartAnimation(anim, pawn, mirror);

                (pawn.jobs.curDriver as JobDriver_Sex).ticks_left = anim.animationTimeTicks;
                (pawn.jobs.curDriver as JobDriver_Sex).sex_ticks = anim.animationTimeTicks;
                (pawn.jobs.curDriver as JobDriver_Sex).duration = anim.animationTimeTicks;
            }
            else
            {
                Log.Message("No animation found");
            }

        }

    }

}


code:
using rjw;
using RJW_ToysAndMasturbation;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Verse;

namespace Patch_SexToysMasturbation
{
    public class AnimSexToyUtility
    {

        public static SexToyAnimationDef tryFindAnimation(CompSexToy sexToy, Pawn pawn)
        {

            IEnumerable<SexToyAnimationDef> options = DefDatabase<SexToyAnimationDef>.AllDefs.Where((SexToyAnimationDef x) =>
            {

                if(!sexToy.Props.requiredBodyParts.Contains(x.requiredBodyPart))
                {
                    return false;
                }

                if(x.requiredBodyPart == "vagina" && !Genital_Helper.has_vagina(pawn))
                {
                    return false;
                }

                return true;

            });

            if(options != null && options.Any())
            {
                return options.RandomElement();
            }
            else
            {
                return null;
            }

        }

    }
}


code:
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Rimworld-Animations")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rimworld-Animations")]
[assembly: AssemblyCopyright("Copyright ©  2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components.  If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("71b05d71-67b2-4014-82cd-18c20ac0882f")]

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]


code:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
  <PropertyGroup>
    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
    <ProjectGuid>{71B05D71-67B2-4014-82CD-18C20AC0882F}</ProjectGuid>
    <OutputType>Library</OutputType>
    <AppDesignerFolder>Properties</AppDesignerFolder>
    <RootNamespace>Rimworld_Animations</RootNamespace>
    <AssemblyName>Rimworld-Animations</AssemblyName>
    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
    <FileAlignment>512</FileAlignment>
    <Deterministic>true</Deterministic>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <DebugSymbols>false</DebugSymbols>
    <DebugType>none</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>1.3\Assemblies\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <DebugType>none</DebugType>
    <Optimize>true</Optimize>
    <OutputPath>bin\Release\</OutputPath>
    <DefineConstants>TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
  </PropertyGroup>
  <ItemGroup>
    <Reference Include="0Harmony">
      <HintPath>..\..\..\..\workshop\content\294100\2009463077\Current\Assemblies\0Harmony.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="AlienRace">
      <HintPath>..\..\..\..\workshop\content\294100\839005762\1.3\Assemblies\AlienRace.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="Assembly-CSharp">
      <HintPath>..\..\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="RJW">
      <HintPath>..\rjw\1.3\Assemblies\RJW.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Net.Http" />
    <Reference Include="System.Xml" />
    <Reference Include="UnityEngine">
      <HintPath>..\..\RimWorldWin64_Data\Managed\UnityEngine.dll</HintPath>
      <Private>False</Private>
    </Reference>
    <Reference Include="UnityEngine.CoreModule">
      <HintPath>..\..\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath>
      <Private>False</Private>
    </Reference>
  </ItemGroup>
  <ItemGroup>
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_Thing.cs" />
    <Compile Include="1.3\Source\Utilities\PatchOperationAddOrReplace.cs" />
    <Compile Include="Properties\AssemblyInfo.cs" />
    <Compile Include="1.3\Source\Actors\Actor.cs" />
    <Compile Include="1.3\Source\Actors\AlienRaceOffset.cs" />
    <Compile Include="1.3\Source\Actors\BodyTypeOffset.cs" />
    <Compile Include="1.3\Source\Animations\AnimationStage.cs" />
    <Compile Include="1.3\Source\Animations\Clips\BaseAnimationClip.cs" />
    <Compile Include="1.3\Source\Animations\Clips\PawnAnimationClip.cs" />
    <Compile Include="1.3\Source\Animations\Clips\ThingAnimationClip.cs" />
    <Compile Include="1.3\Source\Animations\Keyframes\Keyframe.cs" />
    <Compile Include="1.3\Source\Animations\Keyframes\PawnKeyframe.cs" />
    <Compile Include="1.3\Source\Animations\Keyframes\ThingKeyframe.cs" />
    <Compile Include="1.3\Source\Utilities\AnimationUtility.cs" />
    <Compile Include="1.3\Source\Comps\CompBodyAnimator.cs" />
    <Compile Include="1.3\Source\Comps\CompProperties_BodyAnimator.cs" />
    <Compile Include="1.3\Source\Comps\CompProperties_ThingAnimator.cs" />
    <Compile Include="1.3\Source\Comps\CompThingAnimator.cs" />
    <Compile Include="1.3\Source\Defs\AnimationDef.cs" />
    <Compile Include="1.3\Source\MainTabWindows\WorldComponent_UpdateMainTab.cs" />
    <Compile Include="1.3\Source\MainTabWindows\MainTabWindow_OffsetConfigure.cs" />
    <Compile Include="1.3\Source\MainTabWindows\OffsetMainButtonDefOf.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_AlienRace.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_CSL.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_DontShaveYourHead.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_FacialAnimation.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_HatsDisplaySelection.cs" />
    <Compile Include="1.3\Source\Patches\OtherModPatches\HarmonyPatch_ShowHairWithHats.cs" />
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_HeadHair.cs" />
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_PawnRenderer.cs" />
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_PawnRotation.cs" />
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_Pawn_DrawTracker.cs" />
    <Compile Include="1.3\Source\Patches\RimworldPatches\HarmonyPatch_SetPawnAnimatable.cs" />
    <Compile Include="1.3\Source\Patches\Harmony_PatchAll.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_JoinInBed.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseInitiator.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\HarmonyPatch_PlaySexSounds.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\HarmonyPatch_SexTick.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\HarmonyPatch_WorkGiverSex.cs" />
    <Compile Include="1.3\Source\Patches\RJWPatches\JobDrivers\HarmonyPatch_JobDriver_SexBaseReceiverLoved.cs" />
    <Compile Include="1.3\Source\Settings\AnimationSettings.cs" />
  </ItemGroup>
  <ItemGroup>
    <Content Include="1.1\Assemblies\Rimworld-Animations.dll" />
    <Content Include="1.3\Assemblies\Rimworld-Animations.dll" />
    <Content Include="About\About.xml" />
    <Content Include="About\Manifest.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_Beast.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_Lesbian.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_Masturbate.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_Multi.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_vanilla.xml" />
    <Content Include="1.3\Defs\AnimationDefs\Animations_Vanilla2.xml" />
    <Content Include="1.3\Defs\MainTabDefs\MainButtonDef.xml" />
    <Content Include="1.3\Defs\SoundDefs\Sounds_Sex.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\MainButtonDef\MainButtonDef.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Beast.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Lesbian.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_Multi.xml" />
    <Content Include="Languages\PortugueseBrazilian\DefInjected\Rimworld_Animations.AnimationDef\Animations_vanilla.xml" />
    <Content Include="LoadFolders.xml" />
  </ItemGroup>
  <ItemGroup>
    <Folder Include="1.2\" />
    <Folder Include="1.3\Defs\JobDefs\" />
    <Folder Include="1.3\Source\JobDrivers\" />
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>


code:
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29905.134
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rimworld-Animations", "Rimworld-Animations.csproj", "{71B05D71-67B2-4014-82CD-18C20AC0882F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patch_HatsDisplaySelection", "Patch_HatsDisplaySelection\Patch_HatsDisplaySelection.csproj", "{BA766964-1716-422D-A09E-29426F8EB9D5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Patch_SexToysMasturbation", "Patch_SexToysMasturbation\Patch_SexToysMasturbation.csproj", "{87763712-0536-4D5F-9EAA-520F15D4F84E}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Release|Any CPU = Release|Any CPU
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{71B05D71-67B2-4014-82CD-18C20AC0882F}.Release|Any CPU.Build.0 = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{BA766964-1716-422D-A09E-29426F8EB9D5}.Release|Any CPU.Build.0 = Release|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{87763712-0536-4D5F-9EAA-520F15D4F84E}.Release|Any CPU.Build.0 = Release|Any CPU
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
	GlobalSection(ExtensibilityGlobals) = postSolution
		SolutionGuid = {7E01292A-D76F-4CA3-AA64-085B84ECD905}
	EndGlobalSection
EndGlobal


code:
 v1.1.2 [2025-07-15]:
----------------------
+ sbchecklog: -d: display ChangeLog.txt using PAGER.
* remdist: renamed to sbdist.

 v1.1.1a [2025-07-09]:
-----------------------
* sbupdate: add -B to usage.

 v1.1.1 [2025-07-09]:
----------------------
+ simbo: added -o: display relevant environment variables.
+ sbupdate: added -B: like -b, but expects SlackBuilds to blacklist
  as argument (separated by a comma.)

 v1.1.0 [2025-06-18]:
----------------------
+ sbinfo: added -b: show SlackBuild using PAGER.
* simbo: -b: don't print any sbfetch commands in case the binary
  package exists.

 v1.0.9 [2025-06-11]:
----------------------
+ sbfetch: added -f: force download even if distfile already
  exists. Useful in combination with a FETCHCMD supporting resuming.
+ sbfetch: added -i: ignore any download failure (exits with 0.) This
  may be useful when using sbfetch via simbo so that the SlackBuild
  gets run regardless.
* common-funcs.sh: ishtml: use a more exclusive (grep) command to check
  for HTML. Previous versions had sbfetch report that it was unable to
  retrieve `perl-IO-HTML' and `perl-html-tagset'.

 v1.0.8 [2024-09-13]:
----------------------
* Use a variable `VLP' instead of hard-coding `/var/log/packages'.
* simbo: -L: got rid of find(1).

 v1.0.7a [2024-08-21]:
-----------------------
+ sbfetch: added -C. Allows to override FETCHCMD.

 v1.0.7 [2024-08-18]:
----------------------
* sbupdate: -i: more space between name and version.
+ sbinfo: -i: added, displays .info file.
* sbinfo: allow only one of -i, -r, -R to be used.

 v1.0.6 [2024-08-10]:
----------------------
* sbupdate: -ab: don't echo anything in case of blacklisted SlackBuilds.
+ sbinfo.8: added an EXAMPLE section.
+ sbcheckout: -d: added, will `--delete' to RSYNCARGS (was the default.)
* sbcheckout: update (and add to) RSYNCARGS, which now includes all
  options so that the user is able to override them.

 v1.0.5 [2024-08-07]:
----------------------
* common-funcs.sh: check if `kernsb_file' is greater than zero before
  using it.
+ sbinfo, man/sbinfo.8: add option `-p': display category/name only.
* sbinfo.8: -a: improve description.
* sbdep: -E implies -e now.

 v1.0.4 [2024-08-04]:
----------------------
* simbo: check if `kernsb_file' is writable.
* sbinfo: use the shell's substring processing instead of sed(1) to
  remove braces and leading whitespace.
* sbinfo: fix description of SlackBuilds making use of multiple braces,
  like multimedia/intel-gmmlib.
  Output before: `R) Graphics Memory Management Library'
  Output now:    `The Intel(R) Graphics Memory Management Library'

 v1.0.3 [2024-07-22]:
----------------------
sbinfo:
+ -r: display slack-desc
+ -R: display README

 v1.0.2 [2024-07-15]:
----------------------
* sbdep, common-funcs.sh: fix listing of duplicates.

 v1.0.1 [2024-07-13]:
----------------------
* sbinfo: did not output anything when not using `-t'.
+ sbinfo: added `-s': suppress printing of category.
* remdist.8: add note regarding -u.
* simbo: -k: exit with a message in case generation failed.
* simbo.8: updated manpage regarding location of the `kernsb_file.'
* sbdep: fixed a logic bug, `-rs' works correctly now again.

* common-funcs.sh: if $TMP is set, use it instead as location for the
  `kernsb_file'.

 v1.00 [2024-07-11]:
---------------------
* common-funcs.sh: add a function to generate the names of SlackBuilds
  that tag the resutling package with the kernel version. By default, an
  internal copy still gets used, but in case `${TMPDIR:-/tmp}/kernpkgs.simbo'
  exists, it will be used instead.
  Since Slackware does not mount /tmp as tmpfs, this will survive reboots
  in case /etc/rc.d/rc.local_shutdown does not clear it.
+ simbo: add option -k: generate TMP/kernpkgs.simbo.
* sbupdate: -i: changed spacing a litte.

 v0.12a [2024-04-30]:
----------------------
* remdist: use mktemp(1) for all temporary files.

 v0.12 [2024-04-07]:
---------------------
* All: -V:
  only display simbo's version as well as the revision of the
  individual tool invoked.
* sbinfo: remove tmpfile on interrupt.
* sbdep:
  fixed _seen, did not correctly detect duplicates due to
  recent shellcheck changes.
* simbo.8: add sbchecklog(8) to SEE ALSO section.

 v0.11 [2024-03-07]:
---------------------
* Style changes (via shellcheck).
* Updated kernpkgs.
* Fixed BUILD variable being empty.

 v0.10  [2019-10-15]:
----------------------
+ sbupdate: Add a new flag: -b. Ignores SlackBuilds listed in
  SBO_BLACKLIST (space-delimited).

 v0.0.9b [2019-05-28]:
-----------------------
+ simbo: added more SlackBuilds to the kernpkgs variable.

 v0.0.9a [2019-03-21]:
-----------------------
+ remdist: mention new -u option in its manpage.

 v0.0.9  [2019-03-18]:
-----------------------
+ remdist: Added a new option: -u. Removes distfiles of SlackBuilds
  no longer installed on the system.

 v0.0.8a [2018-11-12]:
-----------------------
* sbdep: allow to pass category/name as argument.
* simbo: fix -b not taking care of any non-installed packages.
* simbo: fix -b ignoring non-existing sources.

 v0.0.8  [2018-05-11]:
-----------------------
* All: Changed shebang from ash to dash.
* sbcheckout: treat http/https URLs the same as git://.
+ sbcheckout: Added three environment variables for the corresponding
  git commands (CLEANARGS, CLONEARGS, PULLARGS).
* sbchecklog: allow -C to point to the root directory, too.
+ simbo: new option: -b: use a binary package instead, if it exists.
* Some cosmetic changes to manpages and usage output.

 v0.0.7a [2018-02-19]:
-----------------------
* sbchecklog: remove -v, added -q instead.

 v0.0.7  [2018-02-14]:
-----------------------
- sbcheckout: removed the -c option.
+ sbchecklog: added new tool instead which offers the functionality of
  sbcheckout's -c option (slightly enhanced).
* Some minor changes to the manpages.

 v0.0.6b [2018-02-05]:
-----------------------
+ sbcheckout: New option: -c. Checks if ChangeLog.txt has been updated.
* SlackBuild: rephrased README and slack-desc.

 v0.0.6a [2017-09-20]:
-----------------------
+ Further SlackBuilds have been added which tag their resulting
  package name with the kernel version.
* sbcheckout: updated git:// URL.
* Any URLs have been updated and point to my new domain now.

 v0.0.6  [2017-04-30]:
-----------------------
+ sbinfo: Add a -t flag: search TAGS.txt, if available.

 v0.0.5b [2017-02-03]:
-----------------------
+ simbo: added acpi_call to kernel packages.
* simbo: fixed harmless logic bug.
* Some cosmetic fixes (usages, manpages).
* Updated URLs.
* Updated date and copyright.

 v0.0.5a [2016-09-26]:
-----------------------
* sbupdate: sped up a little by using the shell's substring processing
  instead of rev and cut.
* simbo: work-around for bmake (TAG must not be set).
+ sbinfo: added -v to usage.
* Some small -- tiny, rather -- internal changes and cleanups.

 v0.0.5  [2016-05-18]:
-----------------------
- sbcheckout: removed -r flag. It checks automatically if git or rsync
  is used.
+ sbobsolete: added. A script which lists SBo (or TAG) packages that are
  most likely obsolete.
* sbinfo: fixed wildcard, now something like `sbinfo -a pdf' in a
  directory with files ending in .pdf will work.
* Tiny manpage updates and small code changes `under the hood'.

 v0.0.4a [2016-04-18]:
-----------------------
* simbo: save packages in OUTPUT/ARCH, where ARCH is either x86, x86_64
  or arm (or `unsupported').  I share this via NFS, so simbo would
  install packages for x86_64 on x86, since it does not check for the
  architecture when running upgradepkg.

 v0.0.4  [2016-04-06]:
-----------------------
* Using /usr/SBo as default now instead of /autofs/SBo.
* -p is no longer needed when using xargs and simbo, it detects if a
  category was most likely provided automatically now.
* sbfetch no longer prints name multiple times when using -p in case
  a SlackBuild provides multiple distfiles. Also, -p outputs the
  category part as well, to speed up sbchksum a little bit (so it does
  not have to figure it out using find).
* By default, packages are now saved in a category/program structure
  in /usr/SBo/packages.
* simbo: fixed -r, had to provide CBUILD and correct VERSION values.

 v0.0.3  [2016-04-03]:
-----------------------
+ New tool: sbcheckout. Simple script which will clone (git is
  default) or rsync a SlackBuilds tree from SlackBuilds.org.
* sbupdate's -i option now uses printf which makes for nice, aligned
  output.
* simbo: show any link and/or download commands when using -d, even if
  SlackBuild is already installed.
+ simbo and sbdep now have new -e and -E options: simbo's -e will look
  for .env files in ENVDIR, which can be set using -E or specifying
  ENVDIR. .env files simply list environment variables; simbo will
  output all commands necessary to export them before running a
  SlackBuild, and commands to unset once the SlackBuild finishes.
  sbdep looks for .deps files, which simply list additional
  dependencies that should get pulled in. Some SlackBuilds like ffmpeg
  require both files: list the dependencies you want to include in
  your ffmpeg.deps file, and the necessary environment variables to
  enable them in ffmpeg.env.

 v0.0.2a [2016-04-02]:
-----------------------
* simbo checks if file is html but should not be too now
  (like sbfetch).
+ sbupdate got an -i option: will display the old and new version and
  build number of packages found.
* No longer using ls or grep, which sped up sbupdate and sbdep a
  little bit.
* Fixed simbo -r (did not load info file).
* Fixed sbfetch and sbchksum not displaying usage if called without
  any arguments.
* The SlackBuild will now install profile scripts which set DISTIFLES
  and SLACKBUILDS in the environment. It will use the values you have
  set in your environment, or fall back to the defaults used by simbo.
* Some tiny manpages fixes.

 v0.0.2  [2016-04-01]:
-----------------------
* Fix Makefile; create man dirs. SlackBuild should work now.
* sbchksum now continues if distfiles are missing, but exits with exit
  status 1.
+ sbinfo now supports a -v flag: displays the version provided by the
  SlackBuild.
* Some manpage fixes.

 v0.0.1  [2016-03-31]:
-----------------------
First release.

repo_name: lems/simbo

path: CHANGES


code:
#	$Id: Makefile,v 1.66 2025/07/15 06:31:13 lems Exp $

VERSION = 1.1.2

PREFIX = /usr/local
MANPREFIX = ${PREFIX}/man
EXECPREFIX = ${PREFIX}/libexec/simbo

DOCS =	\
	LICENSE

FUNCS =	\
	checkfuncs.sh	\
	common-funcs.sh

MAN =	\
	sbchecklog.8	\
	sbcheckout.8	\
	sbchksum.8	\
	sbdep.8		\
	sbdist.8	\
	sbfetch.8	\
	sbinfo.8	\
	sbobsolete.8	\
	sbupdate.8	\
	simbo.8

TOOLS =	\
	sbchecklog	\
	sbcheckout	\
	sbchksum	\
	sbdep		\
	sbdist		\
	sbfetch		\
	sbinfo		\
	sbobsolete	\
	sbupdate	\
	simbo

all: install

install:
	@echo installing into ${DESTDIR}${PREFIX}/bin
	mkdir -p ${DESTDIR}${PREFIX}/bin
	for t in $(TOOLS); do \
		sed "s,_EXECPREFIX_,${EXECPREFIX},g" < $$t > ${DESTDIR}${PREFIX}/bin/$$t; \
		chmod 755 ${DESTDIR}${PREFIX}/bin/$$t; \
	done
	mkdir -p ${DESTDIR}${MANPREFIX}/man8
	for m in $(MAN); do \
		sed "s/_VERSION_/${VERSION}/g" < man/$$m > $(DESTDIR)$(MANPREFIX)/man8/$$m; \
		chmod 644 ${DESTDIR}${MANPREFIX}/man8/$$m; \
	done
	@echo installing functions into ${DESTDIR}${EXECPREFIX}/functions.d
	mkdir -p ${DESTDIR}${EXECPREFIX}/functions.d
	for f in $(FUNCS); do \
		sed "s,_VERSION_,${VERSION},g" < $$f > $(DESTDIR)$(EXECPREFIX)/functions.d/$$f; \
		chmod 755 ${DESTDIR}${EXECPREFIX}/functions.d/$$f; \
	done
	@echo installing docs into ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}
	mkdir -p ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}
	for d in $(DOCS); do \
		cp -f $$d ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}; \
	done

uninstall:
	@echo removing from ${DESTDIR}${PREFIX}/bin
	for t in $(TOOLS); do \
		rm -f ${DESTDIR}${PREFIX}/bin/$$t; \
	done
	@echo removing from ${DESTDIR}${MANPREFIX}/man8
	for m in $(MAN); do \
		rm -f ${DESTDIR}${MANPREFIX}/man8/$$m; \
	done
	@echo removing from ${DESTDIR}${EXECPREFIX}/functions.d
	for f in $(FUNCS); do \
		rm -f ${DESTDIR}${EXECPREFIX}/functions.d/$$f; \
	done
	rmdir ${DESTDIR}${EXECPREFIX}/functions.d
	rmdir ${DESTDIR}${EXECPREFIX}
	@echo removing docs from ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}
	for d in $(DOCS); do \
		rm -f ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}/$$d; \
	done
	rmdir ${DESTDIR}${PREFIX}/doc/simbo-${VERSION}

lint:
	for f in $(TOOLS); do \
		shellcheck $$f;	\
	done

path: Makefile


code:
simbo is a collection of scripts for use with SlackBuilds from
SlackBuilds.org.

path: README


code:
simbo consists of several rather simple scripts, each aiming to
only do one thing. They depend on a local copy of the slackbuilds
tree of SlackBuilds.org.

Some of the tasks supported by the tools provided include:

* checking if the ChangeLog has been updated.
* displaying the dependencies of a given SlackBuild.
* retrieving or updating the SlackBuilds.org tree via git or rsync.
* searching for SlackBuilds by name or by making use of TAGS.txt[1].
* fetching all required distfiles of a SlackBuild.
* checking the MD5 hashes of the distfiles fetched.
* checking all installed _SBo packages for updates.
* displaying obsolete _SBo packages that are currently installed.
* cleaning one's distfiles directory from obsolete sources.

The tool `simbo' itself basically only lists the necessary commands
to get a SlackBuild compiled and installed. Its output is usually
piped to bash (/bin/sh).

[1]: TAGS.txt is only available in case one uses rsync to retrieve
     the SlackBuilds.org tree.

path: SlackBuild/README


code:
#!/bin/csh
setenv SBO_MIRROR @SBO_MIRROR@
setenv SBO_CHANGELOG @SBO_CHANGELOG@
setenv DISTFILES @DISTFILES@
setenv ENVDIR @ENVDIR@
setenv OUTPUT @OUTPUT@
setenv SLACKBUILDS @SLACKBUILDS@
setenv FETCHCMD @FETCHCMD@


code:
#!/bin/sh
export SBO_MIRROR=@SBO_MIRROR@
export SBO_CHANGELOG=@SBO_CHANGELOG@
export DISTFILES=@DISTFILES@
export ENVDIR=@ENVDIR@
export OUTPUT=@OUTPUT@
export SLACKBUILDS=@SLACKBUILDS@
export FETCHCMD=@FETCHCMD@


code:
#!/bin/sh

# Slackware build script for simbo

# Copyright 2016, 2017, 2018, 2024 Leonard Schmidt <lems@gmx.net>
# All rights reserved.
#
# Redistribution and use of this script, with or without modification, is
# permitted provided that the following conditions are met:
#
# 1. Redistributions of this script must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

cd $(dirname $0) ; CWD=$(pwd)

PRGNAM=simbo
VERSION=${VERSION:-1.0.8}
BUILD=${BUILD:-1}
TAG=${TAG:-_lems}
PKGTYPE=${PKGTYPE:-tgz}

ARCH=noarch
_ARCH=$(uname -m)

# If the variable PRINT_PACKAGE_NAME is set, then this script will report what
# the name of the created package would be, and then exit. This information
# could be useful to other scripts.
if [ ! -z "${PRINT_PACKAGE_NAME}" ]; then
  echo "$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE"
  exit 0
fi

TMP=${TMP:-/tmp/SBo}
PKG=$TMP/package-$PRGNAM
OUTPUT=${OUTPUT:-/tmp}

DISTFILES=${DISTFILES:-/usr/SBo/distfiles}
ENVDIR=${ENVDIR:-/usr/SBo/options}
FETCHCMD=${FETCHCMD:-wget -q --show-progress -O}
_OUTPUT=${_OUTPUT:-/usr/SBo/packages}
SBO_CHANGELOG=${SBO_CHANGELOG:-https://git.slackbuilds.org/slackbuilds/plain/ChangeLog.txt}
SBO_MIRROR=${SBO_MIRROR:-rsync://slackware.uk/slackbuilds.org/14.2}
SLACKBUILDS=${SLACKBUILDS:-/usr/SBo/slackbuilds}

set -e

rm -rf $PKG
mkdir -p $TMP $PKG $OUTPUT
cd $TMP
rm -rf $PRGNAM-$VERSION
tar xvf $CWD/$PRGNAM-$VERSION.tar.xz || tar xvf $CWD/v$VERSION.tar.xz
cd $PRGNAM-$VERSION
chown -R root:root .
find -L . \
 \( -perm 777 -o -perm 775 -o -perm 750 -o -perm 711 -o -perm 555 \
 -o -perm 511 \) -exec chmod 755 {} \; -o \
 \( -perm 666 -o -perm 664 -o -perm 640 -o -perm 600 -o -perm 444 \
 -o -perm 440 -o -perm 400 \) -exec chmod 644 {} \;

make PREFIX=/usr DESTDIR=$PKG install

# Add profile scripts:
mkdir -p $PKG/etc/profile.d
for file in $(ls $CWD/profile.d/simbo*) ; do
	cat $file | sed -e "s,@DISTFILES@,$DISTFILES,"	\
	-e "s,@ENVDIR@,$ENVDIR,"			\
	-e "s,@FETCHCMD@,$FETCHCMD,"			\
	-e "s,@OUTPUT@,$_OUTPUT,"			\
	-e "s,@SBO_CHANGELOG@,$SBO_CHANGELOG,"		\
	-e "s,@SBO_MIRROR@,$SBO_MIRROR,"		\
	-e "s,@SLACKBUILDS@,$SLACKBUILDS,"		\
	> $PKG/etc/profile.d/$(basename $file)
done
chmod 755 $PKG/etc/profile.d/*

find $PKG/usr/man -type f -exec gzip -9 {} \;
for i in $( find $PKG/usr/man -type l ) ; do ln -s $( readlink $i ).gz $i.gz ; rm $i ; done

mkdir -p $PKG/usr/doc/$PRGNAM-$VERSION
cat $CWD/$PRGNAM.SlackBuild > $PKG/usr/doc/$PRGNAM-$VERSION/$PRGNAM.SlackBuild

mkdir -p $PKG/install
cat $CWD/slack-desc > $PKG/install/slack-desc

cd $PKG
/sbin/makepkg -l y -c n $OUTPUT/$PRGNAM-$VERSION-$ARCH-$BUILD$TAG.$PKGTYPE


code:
PRGNAM="simbo"
VERSION="1.0.8"
HOMEPAGE="https://gitgud.io/lems/simbo"
DOWNLOAD="https://taediumvitae.de/releases/simbo/simbo-1.0.8.tar.gz"
MD5SUM="71aee2cafd6ff9a24f87c626da05370f"
DOWNLOAD_x86_64=""
MD5SUM_x86_64=""
REQUIRES=""
MAINTAINER="Leonard Schmidt"
EMAIL="lems@gmx.net"


code:
# HOW TO EDIT THIS FILE:
# The "handy ruler" below makes it easier to edit a package description.
# Line up the first '|' above the ':' following the base package name, and
# the '|' on the right side marks the last column you can put a character in.
# You must make exactly 11 lines for the formatting to be correct.  It's also
# customary to leave one space after the ':' except on otherwise blank lines.

     |-----handy-ruler------------------------------------------------------|
simbo: simbo (collection of scripts for working with SlackBuilds)
simbo:
simbo: simbo is a collection of scripts for working with a local copy of the
simbo: SlackBuilds.org tree.
simbo:
simbo: Every script performs one task. There is no support for a
simbo: configuration file, environment variables and command-line arguments
simbo: are used instead.
simbo:
simbo: Please check the individual manpages for further details.
simbo:

path: SlackBuild/slack-desc


code:
* Handle office/libreoffice-langpack correctly (simbo, sbupdate).
  Adds a language code to the package name.
* Allow to display changes since last update (sbchecklog).

path: TODO


code:
#! /usr/bin/env dash
#	$Id: checkfuncs.sh,v 1.18 2024/07/06 04:47:06 lems Exp $
#
# Copyright 2016 Leonard Schmidt
# All rights reserved. See the file `LICENSE' for license details.
#

# Set from within sbfetch and sbchksum.
if [ -n "$skipinfo" ]; then
	if [ -z "$DLS" ]; then
		_echo please specify -D
		exit 1
	fi
	if [ -z "$NAME" ]; then
		_echo please specify -N
		exit 1
	fi
	if [ -z "$SUMS" ]; then
		_echo please specify -S
		exit 1
	fi
fi

chkdir DISTFILES "$DISTFILES"
chkdir SLACKBUILDS "$SLACKBUILDS"

SLACKBUILDS=$(echo "$SLACKBUILDS" | sed 's/\/*$//')
DISTFILES=$(echo "$DISTFILES" | sed 's/\/*$//')

tmpd=$(mktemp -d -t "${0##*/}".XXXXXX) || exit 1
md5sums="$tmpd/md5sums"
downloads="$tmpd/downloads"

cleanup() {
	[ -d "$tmpd" ] && rm -f "$tmpd"/*
	rmdir "$tmpd"
	exit "${1:-1}"
}
trap cleanup INT

_load() {
	if [ ! -f "$1" ]; then
		_echo "\`$1' does not exist"
		cleanup 1
	else
		# shellcheck disable=SC1090
		. "$1"
	fi
}

prep_lists() {
	local src md5
	rm -f "$downloads"
	for src in $DL; do
		echo "$src" >> "$downloads"
	done
	rm -f "$md5sums"
	for md5 in $M5; do
		echo "$md5" >> "$md5sums"
	done
}

setup_lists() {
	if [ -n "$NAME" ]; then
		dir="$NAME"
	else
		case "$1" in
		*/*)	dir=$1 ; _load "$SLACKBUILDS/$1/${1##*/}.info" ;;
		*)
			dir=$(find -L "$SLACKBUILDS" -type d -maxdepth 2 -mindepth 2 -name "$1" -printf "%P\\n")
			_load "$SLACKBUILDS/$dir/${dir##*/}.info" ;;
		esac
	fi

	if [ -n "$SUMS" ]; then
		prep_dl "$DLS" "$SUMS"
	else
		prep_dl
	fi
	prep_lists

	msize=$(wc -l < "$md5sums")
	dsize=$(wc -l < "$downloads")
	if [ "$msize" != "$dsize" ]; then
		_echo "$dir: number of recorded distfiles and md5sums do not match"
		cleanup 1
	fi
}


code:
#! /usr/bin/env dash
#	$Id: common-funcs.sh,v 1.55 2025/07/09 05:12:21 lems Exp $
#
# Copyright 2016 Leonard Schmidt
# All rights reserved. See the file `LICENSE' for license details.
#

# Some common variables used by most of the scripts
DISTFILES=${DISTFILES:-/usr/SBo/distfiles}
ENVDIR=${ENVDIR:-/usr/SBo/options}
SLACKBUILDS=${SLACKBUILDS:-/usr/SBo/slackbuilds}
TAG=${TAG:-_SBo}
VLP=/var/log/packages

kernver=$(uname -r | tr - _)
ARCH=${ARCH:-$(uname -m)}

case "$ARCH" in
i?86)	_ARCH=x86 ;;
x86_64)	_ARCH=x86_64 ;;
arm*)	_ARCH=arm ;;
*)	_ARCH=unsupported ;;
esac

_verbose() { [ -n "$verbose" ] && echo "${0##*/}: $*" >&2 ; }
_ls() { ls /var/log/packages/"$1" 1>/dev/null 2>/dev/null ; }
_echo() { echo "${0##*/}: $*" >&2 ; }

# List of SlackBuilds that tag the resulting package with the
# output of uname -r.
kernpkgs="perf
digimend-kernel-drivers
klibc
beegfs
broadcom-wl
r8168
rtl8188eu
rtl8188fu
rtl8812bu
rtl8821ce
xtables-addons
acpi_call
atarisio
chipsec
evdi-kernel
hp-wmi-sensors
netatop
nvidia-kernel
nvidia-legacy340-kernel
nvidia-legacy390-kernel
nvidia-legacy470-kernel
nvidia-open-kernel
openrazer-kernel
openzfs
realtek-bt
steamos-xpad
sysdig
tp_smapi
v4l2loopback
vhba-module
virtualbox-kernel"

# If such a file exists, use it instead.
: "${TMPDIR:=$TMP}"
kernsb_file="${TMPDIR:-/tmp}/kernpkgs.simbo"
if [ -s "$kernsb_file" ]; then
	kernpkgs="$(cat "$kernsb_file")"
fi

gen_kernpkgs() {
	grep 'uname -r'	\
	"$SLACKBUILDS"/*/*/*.SlackBuild	\
	| sed	\
	-e 's/:.*//g'	\
	-e 's/\// /g' 	\
	| rev		\
	| awk '{print $2}'	\
	| rev > "$kernsb_file"
}

chkdir() {
	if [ ! -d "$2" ]; then
		_echo "$1: $2 does not exist"
		exit 1
	fi
}

getver() {
	# remove path
	oldver=${1##*/}
	# remove build+tag
	oldver=${oldver%-*}
	# remove arch
	oldver=${oldver%-*}
	# get basename
	basnam=${oldver%-*}
	# version
	oldver=${oldver#"$basnam"-*}

	echo "$oldver"
}

getbld() {
	if [ "$1" = -B ]; then
		shift
		eval C"$(grep ^BUILD "$1")"
	else
		# Gets BUILD from built package.

		# remove path
		oldbld=${1##*/}
		# remove tag
		oldbld=${oldbld%*"$TAG"}
		# get build number
		oldbld=${oldbld##*-}

		echo "$oldbld"
	fi
}

_version() {
	prog=${0##*/}
	echo "$prog _VERSION_ [rev. $(sed -n 2p "$(command -v "$prog")"	\
	 | awk '{print $4}')]" >&2
	exit 1
}

_seen() {
	val="$1"
	shift
	for i in $@; do
		[ "$i" = "$val" ] && return 0
	done
	return 1
}

_source() {
	if [ -f "$1" ]; then
		. "$1"
	else
		_echo "$1 does not exist"
		exit 1
	fi
}

_whence() {
	command -v "$1" >&2 1>/dev/null &&	\
	return 0 || return 1
}

ishtml() {
	ext=${1##*/}
	ext=${ext##*.}
	case "$ext" in
	[Hh][Tt][Mm]*)
		return 1
		;;
	*)
		if file "$1"	\
		| cut -f2 -d:	\
		| grep -q ' HTML Document,'; then
			return 0
		else
			return 1
		fi
		;;
	esac
}

setup_vals() {
	nam="$1"

	# Assigned inside sbdep.
	if [ -n "$short" ]; then
		_source "$SLACKBUILDS/"*/"$nam/$nam.info"
		getbld -B "$SLACKBUILDS/"*/"$PRGNAM/$PRGNAM.SlackBuild"
	else
		_source "$SLACKBUILDS/$nam/${nam##*/}.info"
		getbld -B "$SLACKBUILDS/$nam/$PRGNAM.SlackBuild"
	fi

	if _seen "$PRGNAM" "$kernpkgs"; then
		VERSION=${VERSION}_$kernver
	fi
}

prep_dl() {
	# shellcheck disable=2034
	if [ -z "$1" ]; then
		# DOWNLOAD_x86_64 sourced via .info file.
		if [ "$ARCH" = x86_64 ] && [ -n "$DOWNLOAD_x86_64" ]; then
			case "${DOWNLOAD_x86_64%%:*}" in
			http|https|ftp)
				DL=$DOWNLOAD_x86_64
				M5=$MD5SUM_x86_64
				;;
			*)
				_echo "$PRGNAM does not support $ARCH"
				exit 1
				;;
			esac
		else
			case "${DOWNLOAD%%:*}" in
			http|https|ftp)
				DL=$DOWNLOAD
				M5=$MD5SUM
				;;
			*)
				_echo "$PRGNAM does not support $ARCH"
				exit 1
				;;
			esac
		fi
	else
		DL="$1"
		M5="$2"
	fi
}


code:
.\" Man page for sbchecklog.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBCHECKLOG 8  "July 14, 2025" "version _VERSION_"
.SH NAME
sbchecklog \- check ChangeLog.txt for updates
.SH SYNOPSIS
.B sbchecklog
[OPTS]
.SH DESCRIPTION
.I sbchecklog
checks the remote ChangeLog against the local one (if non-zero)
for updates, exiting with 0 in case of updates, 1 otherwise.
Requires curl or wget.
.SH OPTIONS
.TP
.BR \-C
Specify URL pointing directly to ChangeLog.txt (or its root directory), default:
.I https://git.slackbuilds.org/slackbuilds/plain/ChangeLog.txt\fR.
.TP
.BR \-d
Display ChangeLog.txt using PAGER, defaulting to
.I less(1)\fR.
.TP
.BR \-q
Quiet, no output.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbchecklog
honors the following environment variables:
.PP
.B SBO_CHANGELOG
.PP
.B SLACKBUILDS
.PP
See the above-mentioned options
.B \-C
and
.B \-S
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically.
.SH EXAMPLES
The following command will update the local slackbuilds tree
only in case the ChangeLog has been updated remotely:
.IP
.B sbchecklog && sbcheckout
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbcheckout (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbcheckout.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBCHECKOUT 8  "August 8, 2024" "version _VERSION_"
.SH NAME
sbcheckout \- receives and updates the SlackBuilds.org tree via rsync or git
.SH SYNOPSIS
.B sbcheckout
[OPTS]
.SH DESCRIPTION
.I sbcheckout
uses git (default) or rsync to get the slackbuilds tree
from SlackBuilds.org.
.SH OPTIONS
.TP
.BR \-d
Add
.I --delete
to
.B RSYNCARGS.
.TP
.BR \-M
Specify a different mirror to use, default
.I git://git.slackbuilds.org/slackbuilds\fR.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbcheckout
honors the following environment variables (first three used by git):
.PP
.B CLEANARGS
(Default:
.I -d -f\fR)
.PP
.B CLONEARGS
(Default:
.I --depth 1\fR)
.PP
.B PULLARGS
(Default:
.I --stat\fR)
.PP
.B RSYNCARGS
(Default:
.I -haP --no-o --no-g --safe-links --timeout=60 --contimeout=30 --info=progress2,name0\fR)
.PP
.B SBO_MIRROR
.PP
.B SLACKBUILDS
.PP
See the above-mentioned options
.B \-M
and
.B \-S
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically.
.SH EXAMPLES
The following command will update the local slackbuilds tree
only in case the ChangeLog has been updated remotely:
.IP
.B sbchecklog && sbcheckout
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbchecklog (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbchksum.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBCHKSUM 8  "April 4, 2016" "version _VERSION_"
.SH NAME
sbchksum \- compare recorded and real MD5 hashes of distfiles (SlackBuilds.org)
.SH SYNOPSIS
.B sbchksum
[OPTS] [PRGNAM [PRGNAM2 [...]]]
.SH DESCRIPTION
.I sbchksum
checks the MD5 hashes of all distfiles belonging to PRGNAM against
those recorded in PRGNAM.info.
.SH OPTIONS
.TP
.BR \-c
Distfiles directory does not use a category/program structure.
.TP
.BR \-D
Path to distfiles directory, default:
.I /usr/SBo/distfiles\fR.
.TP
.BR \-M
This expects the value of MD5SUM/MD5SUM_x86_64 as argument.
This option is meant for use with
.BR simbo (8)\fR.
.TP
.BR \-N
This expects category/name as argument. This option is meant
for use with
.I simbo\fR.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-U
This expects the value of DOWNLOAD/DOWNLOAD_x86_64 as
argument. This option is meant for use with
.I simbo\fR.
.TP
.BR \-v
Be verbose: outputs OK or FAILED with path to distfile.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbchksum
honors the following environment variables:
.PP
.B DISTFILES
.PP
.B SLACKBUILDS
.PP
See the above-mentioned options
.B \-D
and
.B \-S
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically.
.SH EXAMPLES
To download and check a SlackBuild's distfiles including all of
its dependencies, issue:
.IP
.B sbdep -r PRGNAM | xargs sbfetch -p | xargs sbchksum -v
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbdep (8),
.BR sbfetch (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbdep.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBDEP 8  "August 7, 2024" "version _VERSION_"
.SH NAME
sbdep \- show dependencies of the given SlackBuild
.SH SYNOPSIS
.B sbdep
[OPTIONS] PRGNAM [PRGNAM2 [...]]
.SH DESCRIPTION
.I sbdep
is a tool which outputs the dependencies of the SlackBuild(s)
specified.
.SH OPTIONS
.TP
.BR \-e
Look for a file called
.I PRGNAM.deps
in
.B ENVDIR\fR,
and add any programs that are listed therein as dependencies.
.TP
.BR \-E
Path to directory with .deps and .env files, default:
.I /usr/SBo/options\fR.
Implies
.BR \-e\fR.
.TP
.BR \-r
Display all dependencies of a SlackBuild, even those that are
already installed.
.TP
.BR \-s
Print only the name of the SlackBuild, not its category.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-T
The tag used to check for installed packages, default:
.I _SBo\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbdep
honors the following environment variables:
.PP
.B ENVDIR
.PP
.B SLACKBUILDS
.PP
.B TAG
.PP
See the above-mentioned options
.B \-E\fR,
.B \-S
and
.B \-T
for the default values used.
.PP
.I NOTE:
Trailing slashes will be removed automatically.
.SH EXAMPLES
.I sbdep's
output may be used with
.BR simbo (8)
to resolve dependencies, if that's desired.
.IP
.B sbdep PRGNAM | xargs simbo -a | sh
.PP
To rebuild a SlackBuild and all of its dependencies:
.IP
.B sbdep -r PRGNAM | xargs simbo -r | sh
.PP
To download and check all sources of a SlackBuild:
.IP
.B sbdep -r PRGNAM | xargs sbfetch -p | xargs sbchksum -v
.PP
To get an overview of a SlackBuild's dependencies, it's fastest to use
.B -sr\fR.
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbchksum (8),
.BR sbfetch (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbdist.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH sbdist 8  "July 13, 2024" "version _VERSION_"
.SH NAME
sbdist \- display obsolete distfiles (SlackBuilds.org)
.SH SYNOPSIS
.B sbdist
[OPTS]
.SH DESCRIPTION
.I sbdist
will output all distfiles it deems obsolete. If distfiles
are saved in subdirectories (category/program),
.I sbdist
checks the
.I \.info
files of those programs found in your distfiles directory.
Otherwise, all packages of tag
.I _SBo
are checked.
.SH OPTIONS
.TP
.BR \-c
Distfiles directory does not use a category/program
structure.
.TP
.BR \-D
Specify the path to your distfiles directory, default:
.I /usr/SBo/distfiles\fR.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-T
Tag to use when using
.B \-c\fR,
default:
.I _SBo\fR.
.TP
.BR \-u
Remove all distfiles of SlackBuilds no longer installed.
Since this checks the currently installed SlackBuilds, distfiles will end
up being displayed that may have been installed on another machine (in case
you share them via NFS or some other means.)
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbdist
honors the following environment variables:
.PP
.B DISTFILES
.PP
.B SLACKBUILDS
.PP
.PP
.B TAG
.PP
See the above-mentioned options
.B \-D\fR,
.B \-S
and
.B \-T
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically.
.SH EXAMPLES
.I sbdist
only outputs absolute paths of out-of-date distfiles it
found. To remove them, one would use
.BR xargs (1)\fR:
.IP
.B sbdist | xargs rm -f
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbfetch (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbfetch.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBFETCH 8  "June 11, 2025" "version _VERSION_"
.SH NAME
sbfetch \- download distfiles (SlackBuilds.org)
.SH SYNOPSIS
.B sbfetch
[OPTS] [PRGNAM [PRGNAM2 [...]]]
.SH DESCRIPTION
.I sbfetch
downloads all distfiles of the SlackBuild specified. If the
master site fails, it tries several other sites to retrieve
the file.
.SH OPTIONS
.TP
.BR \-c
Distfiles directory does not use a category/program structure.
.TP
.BR \-C
Override
.I FETCHCMD\fR.
.TP
.BR \-D
Path to distfiles directory, default:
.I /usr/SBo/distfiles\fR.
.TP
.BR \-i
Exits with 0 regardless of any download failure (1 otherwise.) This may
be useful in case the check for files being HTML fails so that
.BR simbo (8)\fR
continues running the SlackBuild.
.TP
.BR \-f
Force download in case the distfile already exists.
This may be used to continue downloading a partially downloaded file.
See the
.I -c\fR
and
.I -C\fR
flags of
.I wget(1)\fR
and
.I curl(1)\fR,
respectively.
.TP
.BR \-M
This expects the value of MD5SUM/MD5SUM_x86_64 as argument.
This option is meant for use with
.BR simbo (8)\fR.
.TP
.BR \-N
This expects category/name as argument. This option is meant
for use with
.I simbo\fR.
.TP
.BR \-p
Output the name of the SlackBuilds processed. This may be used
with
.BR xargs (1)\fR.
See the
.B EXAMPLES
section for a possible use case.
.TP
.BR \-s
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-U
This expects the value of DOWNLOAD/DOWNLOAD_x86_64 as
argument. This option is meant for use with
.I simbo\fR.
.TP
.BR \-v
Output the URL used to retrieve the file; also print if the
file already exists.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbfetch
honors the following environment variables:
.PP
.B FETCHCMD
(Default:
.I wget -q --show-progress -O\fR)
.PP
.B DISTFILES
.PP
.B SLACKBUILDS
.PP
See the above-mentioned options
.B \-D
and
.B \-S
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically.
.PP
.B FETCHCMD
needs to end with the -o or -O (output) option of your
download command.
.SH EXAMPLES
To download and check a SlackBuild's distfiles including all of
its dependencies, issue:
.IP
.B sbdep -r PRGNAM | xargs sbfetch -p | xargs sbchksum -v
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbdep (8),
.BR sbchksum (8),
.BR sbdist (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbinfo.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBINFO 8  "June 13, 2025" "version _VERSION_"
.SH NAME
sbinfo \- display one-line description for each SlackBuild (SlackBuilds.org)
.SH SYNOPSIS
.B sbinfo
[OPTS] PRGNAM [PRGNAM2 [...]]
.SH DESCRIPTION
.I sbinfo
displays a one-line description about the program given as argument,
taken from its
.I slack-desc
file
.SH OPTIONS
.TP
.BR \-a
Uses wildcards around argument passed (*name*), thereby producing more matches.
.TP
.BR \-b
Show
.B SlackBuild\fR.
Uses
.B PAGER\fR,
falls back to
.BR cat (1)
otherwise.
.TP
.BR \-i
Show
.B .info
file. Uses
.B PAGER\fR,
falls back to
.BR cat (1)
otherwise.
.TP
.BR \-p
Display category/name only.
.TP
.BR \-r
Show
.B slack-desc
file. Uses
.B PAGER\fR,
falls back to
.BR cat (1)
otherwise.
.TP
.BR \-R
Show
.B README\fR.
Uses
.B PAGER\fR,
falls back to
.BR cat (1)
otherwise.
.TP
.BR \-s
Suppress category.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-t
Search TAGS.txt. Available if rsync is used. Specify twice to
colourize output.
.TP
.BR \-v
Display version of PRGNAM as well.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbinfo
honors the following environment variables:
.PP
.B SLACKBUILDS
.PP
See the above-mentioned
.B \-S
option for the default value used.
.PP
.I NOTE\fR:
Trailing slashes will be removed automatically from
.I SLACKBUILDS\fR.
.SH EXAMPLES
.B sbinfo vile
.TP
development/vile: VI Like Emacs
.PP
.B sbinfo -v vile
.TP
development/vile (9.8z): VI Like Emacs
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbobsolete.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBOBSOLETE 8  "April 30, 2016" "version _VERSION_"
.SH NAME
sbobsolete \- check if obsolete packages are installed (SlackBuilds.org)
.SH SYNOPSIS
.B sbobsolete
[OPTS]
.SH DESCRIPTION
By default,
.I sbobsolete
checks all packages with the tag
.B _SBo\fR.
.SH OPTIONS
.TP
.BR \-S
Specify the path to your slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-T
Specify the tag to use, default:
.I _SBo\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbobsolete
honors the following environment variables:
.PP
.B SLACKBUILDS
.PP
.B TAG
.PP
See the above-mentioned options
.B \-S
and
.B \-T
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes are removed automatically from the value
passed to
.B SLACKBUILDS\fR.
.SH EXAMPLES
.I sbobsolete
only outputs the bare program names of obsolete programs found.
You may feed its output to
.BR removepkg (8)
by using command substitution or
.BR xargs (1)\fR.
For example:
.IP
.B sbobsolete | xargs removepkg
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sboget (8).
.
.


code:
.\" Man page for sbupdate.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SBUPDATE 8  "July 09, 2025" "version _VERSION_"
.SH NAME
sbupdate \- check if updated SlackBuilds are available (SlackBuilds.org)
.SH SYNOPSIS
.B sbupdate
[OPTS]
.SH DESCRIPTION
By default,
.I sbupdate
checks all packages with the tag
.B _SBo
for updates.
.SH OPTIONS
.TP
.BR \-a
Echo basename of every TAG package installed.
.TP
.BR \-b
Ignore blacklisted packages. See
.I ENVIRONMENT
below.
.TP
.BR \-B
Pass SlackBuilds to be blacklisted, separated by a comma.
.TP
.BR \-i
Display old and new version (including build number) of the
packages found.
.TP
.BR \-S
Specify the path to your slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-T
Specify the tag to use, default:
.I _SBo\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I sbupdate
honors the following environment variables:
.PP
.B SBO_BLACKLIST
Space-separated list of SlackBuilds to ignore. See
.I -b\fR.
.PP
.B SLACKBUILDS
.PP
.B TAG
.PP
See the above-mentioned options
.B \-S
and
.B \-T
for the default values used.
.PP
.I NOTE\fR:
Trailing slashes are removed automatically from the value
passed to
.B SLACKBUILDS\fR.
.SH EXAMPLES
.I sbupdate
only outputs the bare program names of updates found. You may feed
its output to
.I simbo
by using command substitution or
.BR xargs (1)\fR.
.PP
The following example uses
.I sbdep
to catch any new dependencies, and feeds the resulting
output to
.I simbo\fR:
.IP
.B sbupdate | xargs sbdep | xargs simbo
.PP
To actually take action, one may pipe
.I simbo's
output to
.BR sh (1)\fR:
.IP
.B sbupdate | xargs sbdep | xargs simbo | sh
.PP
An experimental command to rebuild all packages:
.IP
.B sbupdate -a | xargs simbo -r | sh
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR simbo (8),
.BR sbdep (8),
.BR sboget (8).
.
.


code:
.\" Man page for simbo.
.\" Written by Leonard Schmidt <lems@gmx.net>.
.TH SIMBO 8  "July 01, 2025" "version _VERSION_"
.SH NAME
simbo \- small and simple SlackBuilds.org build tool
.SH SYNOPSIS
.B simbo
[OPTIONS] PRGNAM [PRGNAM2 [...]]
.SH DESCRIPTION
.I simbo
lists the necessary commands to get a SlackBuild from SlackBuilds.org
built and installed.  No configuration except specifying one's distfiles
and SlackBuilds directories is necessary. It does not support
downloading SlackBuilds tarballs, one needs to have a SlackBuilds tree
available instead.
.SH OPTIONS
.TP
.BR \-a
This also connects the mkdir, link, fetch and checksum commands using a
logical AND.
.TP
.BR \-b
Use a binary package instead, in case it exists.
.TP
.BR \-c
Don't use a category/program structure in your distfiles and
packages directory.
.TP
.BR \-C
Show command to check MD5 hashes of distfiles using
.BR sbchksum (8)\fR.
.TP
.BR \-d
Don't show any build commands, only those that would download all
required distfiles and make the necessary links.
.TP
.BR \-D
Specify the path to your distfiles directory, default:
.I /usr/SBo/distfiles\fR.
Distfiles can be shared between
.I sboget
and
.I simbo\fR,
as both use the same directory structure.
.TP
.BR \-e
Look for
.I PRGNAM.env
in
.I ENVDIR
and set any environment variables contained therein.
.TP
.BR \-E
Specify path to
.I ENVDIR\fR,
default:
.I /usr/SBo/options\fR.
.TP
.BR \-f
Use fakeroot and sudo.
.TP
.BR \-g
Output commands to get all distfiles first, then output build and install
commands.
.TP
.BR \-H
Divide commands using hyphens.
.TP
.BR \-k
Generates a list with names of SlackBuilds that tag the resulting
packages with the kernel version. Will use
.B TMP
if set, gets otherwise saved to
.I ${TMPDIR:-/tmp}/kernpkgs.simbo\fR.
An internal copy exists, which gets used by default, but may be a bit
outdated.
.TP
.BR \-L
Only list the directory contents -- absolute paths -- of the SlackBuild(s)
specified.
.TP
.BR \-o
Display relevant environment variables as well as their values.
.TP
.BR \-r
Show commands for SlackBuilds that are already installed.
.TP
.BR \-S
Path to slackbuilds directory, default:
.I /usr/SBo/slackbuilds\fR.
.TP
.BR \-v
For
.B \-C\fR:
pass
.B \-v
to
.I sbchksum\fR.
.TP
.BR \-V
Print version information.
.SH ENVIRONMENT
.I simbo
honors the following environment variables:
.PP
.B DISTFILES
(Default:
.I /usr/SBo/distfiles\fR)
.PP
.B ENVDIR
(Default:
.I /usr/SBo/options\fR)
.PP
.B OUTPUT
(Default:
.I /usr/SBo/packages\fR)
.PP
.B SLACKBUILDS
(Default:
.I /usr/SBo/slackbuilds\fR)
.PP
.B TAG
(Default:
.I _SBo\fR)
.PP
.B TMP
(Default:
.I /tmp/_SBo\fR)
.PP
Trailing slashes are automatically removed from variables that
take directories as values.
.SH EXAMPLES
One usually pipes
.I simbo's
output to
.BR sh (1)\fR.
To install
.I PRGNAM\fR,
one would execute:
.IP
.B simbo PRGNAM | sh
.PP
To log all output to a file with the current date in its name, execute:
.IP
.B simbo PRGNAM | sh &>/tmp/simbo-$(date +%F).log
.SH AUTHOR
Leonard Schmidt <lems@gmx.net>
.SH SEE ALSO
.BR upgradepkg (8),
.BR sbdist (8),
.BR sbchecklog (8),
.BR sbcheckout (8),
.BR sbchksum (8),
.BR sbdep (8),
.BR sbfetch (8),
.BR sbinfo (8),
.BR sbobsolete (8),
.BR sbupdate (8),
.BR sboget (8).
.
.


code:
#! /usr/bin/env sh
#	$Id: sbchecklog,v 1.11 2025/07/14 16:05:53 lems Exp $
#
# Copyright 2018 Leonard Schmidt
# All rights reserved. See the file `LICENSE' for license details.
#

pager=${PAGER:-less}

# shellcheck disable=SC2043,SC1090
for _func in common-funcs.sh; do
	[ -f "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func" ] &&	\
	. "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func"
done

SBO_CHANGELOG=${SBO_CHANGELOG:-https://git.slackbuilds.org/slackbuilds/plain/ChangeLog.txt}

usage() {
cat << EOF
usage: ${0##*/} [OPTS]
-C     URL pointing to ChangeLog.txt
-d     display ChangeLog.txt using \$PAGER ($pager)
-q     quiet, no output
-S     path to slackbuilds directory
-V     print version information
EOF
	exit 1
}

while getopts C:dqS:V f 2>/dev/null; do
	case "$f" in
	C)	SBO_CHANGELOG=$OPTARG ;;
	d)	display=1 ;;
	q)	quiet=1 ;;
	S)	SLACKBUILDS=$OPTARG ;;
	V)	_version ;;
	\?)	usage ;;
	esac
done
shift $((OPTIND - 1))

# This allows pointing to either ChangeLog.txt directly,
# or to the root dir.
case "$SBO_CHANGELOG" in
*/ChangeLog.txt)
	;;
*)
	SBO_CHANGELOG=$SBO_CHANGELOG/
	SBO_CHANGELOG=${SBO_CHANGELOG%/*}
	SBO_CHANGELOG=$(echo "$SBO_CHANGELOG"/ | sed 's/\/*$//')
	SBO_CHANGELOG=$SBO_CHANGELOG/ChangeLog.txt
	;;
esac

SLACKBUILDS=$(echo "$SLACKBUILDS" | sed 's/\/*$//')
chlog="$SLACKBUILDS/ChangeLog.txt"

if [ "$display" = 1 ]; then
	if [ -e "$chlog" ]; then
		$pager "$chlog" || exit 1
		exit 0
	else
		_echo "\`$chlog' does not exist"
		exit 1
	fi
fi

if _whence curl; then
	FETCHCMD='curl -sL'
elif _whence wget; then
	FETCHCMD='wget -qO-'
else
	_echo wget or curl required
	exit 1
fi

if [ -s "$chlog" ]; then
	cur_date=$(sed 1q "$chlog")
	new_date=$($FETCHCMD "$SBO_CHANGELOG" | sed 1q)
	if [ "$cur_date" = "$new_date" ]; then
		[ -n "$quiet" ] || _echo ChangeLog.txt: no updates
		exit 1
	else
		[ -n "$quiet" ] || _echo "$cur_date -> $new_date"
		exit 0
	fi
else
	_echo "\`$chlog' does not exist"
	exit 1
fi

path: sbchecklog


code:
#! /usr/bin/env dash
#	$Id: sbcheckout,v 1.21 2025/07/09 05:12:21 lems Exp $
#
# Copyright 2016 Leonard Schmidt
# All rights reserved. See the file `LICENSE' for license details.
#

# shellcheck disable=SC2043,SC1090
for _func in common-funcs.sh; do
	[ -f "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func" ] &&	\
	. "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func"
done

CLEANARGS=${CLEANARGS:-"-d -f"}
CLONEARGS=${CLONEARGS:-"--depth 1"}
PULLARGS=${PULLARGS:-"--stat"}
RSYNCARGS=${RSYNCARGS:-"-haP		\
			--no-o		\
			--no-g		\
			--safe-links	\
			--timeout=60	\
			--contimeout=30	\
			--info=progress2,name0"}
SBO_MIRROR=${SBO_MIRROR:-git://git.slackbuilds.org/slackbuilds}

chk_src() {
	case "${SBO_MIRROR%%:*}" in
	"$1")	return 0 ;;
	*)	return 1 ;;
	esac
}

usage() {
cat << EOF
usage: ${0##*/} [OPTS]
-d     rsync: use --delete
-M     URL to use [$SBO_MIRROR]
-S     path to slackbuilds directory
-V     print version information
EOF
	exit 1
}

while getopts dM:S:V f 2>/dev/null; do
	case "$f" in
	d)	RSYNCARGS="--delete $RSYNCARGS" ;;
	M)	SBO_MIRROR=$OPTARG ;;
	S)	SLACKBUILDS=$OPTARG ;;
	V)	_version ;;
	\?)	usage ;;
	esac
done
shift $((OPTIND - 1))

SLACKBUILDS=$(echo "$SLACKBUILDS" | sed 's/\/*$//')
SBO_MIRROR=$(echo "$SBO_MIRROR" | sed 's/\/*$//')

# shellcheck disable=SC2086
if chk_src git || chk_src https || chk_src http; then
	if [ -d "$SLACKBUILDS/.git" ]; then
		cd "$SLACKBUILDS" || exit 1
		git pull $PULLARGS || exit 1
		git clean $CLEANARGS || exit 1
	elif [ -d "$SLACKBUILDS" ]; then
		_echo "$SLACKBUILDS/.git does not exist; is this a git repository?"
		exit 1
	else
		mkdir -p "$SLACKBUILDS" || exit 1
		git clone $CLONEARGS "$SBO_MIRROR"	\
		"$SLACKBUILDS" || exit 1
	fi
elif chk_src rsync; then
	if [ ! -d "$SLACKBUILDS" ]; then
		mkdir -p "$SLACKBUILDS" || exit 1
	fi
	rsync $RSYNCARGS "$SBO_MIRROR"/ "$SLACKBUILDS" || exit 1
else
	_echo SBO_MIRROR seems to be neither git nor rsync
	exit 1
fi

path: sbcheckout


code:
#! /usr/bin/env dash
#	$Id: sbchksum,v 1.27 2025/07/03 18:27:23 lems Exp $
#
# Copyright 2016 Leonard Schmidt
# All rights reserved. See the file `LICENSE' for license details.
#

# shellcheck disable=SC2043,SC1090
for _func in common-funcs.sh; do
	[ -f "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func" ] &&	\
	. "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/$_func"
done

usage() {
cat << EOF
usage: ${0##*/} [OPTS] [PRGNAM [PRGNAM2 [...]]]
-c     distfiles don't use a category/program structure
-D     path to distfiles directory
-M     the MD5SUM/MD5SUM_x86_64 value (simbo)
-N     the category/name part (simbo)
-S     path to slackbuilds directory
-U     the DOWNLOAD/DOWNLOAD_x86_64 value (simbo)
-v     print OK/FAILED
-V     print version information
EOF
	exit 1
}

while getopts cD:M:N:S:U:vV f 2>/dev/null; do
	# (Used via checkfuncs.sh.)
	# shellcheck disable=SC2034
	case "$f" in
	c)	nosub=1 ;;
	D)	DISTFILES=$OPTARG ;;
	M)	SUMS=$OPTARG ; skipinfo=1 ;;
	N)	NAME=$OPTARG ; skipinfo=1 ;;
	S)	SLACKBUILDS=$OPTARG ;;
	U)	DLS=$OPTARG ; skipinfo=1 ;;
	v)	verbose=1 ;;
	V)	_version ;;
	\?)	usage ;;
	esac
done
shift $((OPTIND - 1))

if [ $# = 0 ]; then
	[ -z "$skipinfo" ] && usage
fi

_source "${SIMBO_FUNCS:-_EXECPREFIX_/functions.d}/checkfuncs.sh"

# shellcheck disable=SC2086
[ -n "$NAME" ] && set -- $NAME
exitstatus=0

# shellcheck disable=SC2086,SC2154
for prog; do
	setup_lists $prog
	count=0
	# (checkfuncs.sh)
	while read -r dl; do
		source="${dl##*/}"
		count=$(( count + 1 ))
		[ -n "$nosub" ] && dist="$DISTFILES/$source" ||	\
		dist="$DISTFILES/$dir/$source"

		if [ ! -f "$dist" ]; then
			_verbose "$dist does not exist"
			exitstatus=1
			continue
		fi

		real_md5=$(md5sum "$dist" | cut -f1 -d ' ')
		recd_md5=$(sed -n "${count}"p "$md5sums")
		if [ "$real_md5" != "$recd_md5" ]; then
			_verbose "FAILED: $dist"
			exitstatus=1
		else
			_verbose "OK: $dist"
		fi
	done < "$downloads"
done
cleanup "$exitstatus"

path: sbchksum